]> glassweightruler.freedombox.rocks Git - Ventoy.git/commitdiff
Experimental Linux GUI based on web browser
authorlongpanda <admin@ventoy.net>
Fri, 26 Feb 2021 13:36:53 +0000 (21:36 +0800)
committerlongpanda <admin@ventoy.net>
Fri, 26 Feb 2021 13:36:53 +0000 (21:36 +0800)
158 files changed:
INSTALL/README
INSTALL/VentoyWeb.sh [new file with mode: 0644]
INSTALL/ventoy_pack.sh
LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_define.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_json.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_json.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_log.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_util.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Core/ventoy_util.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/handle_form.inl [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/md5.inl [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_duktape.inl [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_lua.inl [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/include/timer.inl [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/libhttp/libhttp-1.8.tar.gz [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/COPYING [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/README [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/Documentation/xz.txt [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/decompress/unxz.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/xz.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/decompress_unxz.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Kconfig [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Makefile [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc32.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc64.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_bcj.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_stream.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_syms.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_test.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_lzma2.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_private.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_stream.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/scripts/xz_wrap.sh [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/boottest.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/buftest.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/bytetest.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xz_config.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xzminidec.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Web/ventoy_http.c [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/Web/ventoy_http.h [new file with mode: 0644]
LinuxGUI/Ventoy2Disk/main.c [new file with mode: 0644]
LinuxGUI/WebUI/favicon.ico [new file with mode: 0644]
LinuxGUI/WebUI/index.html [new file with mode: 0644]
LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/AdminLTE/js/app.min.js [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 [new file with mode: 0644]
LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js [new file with mode: 0644]
LinuxGUI/WebUI/static/css/font-awesome.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/css/ionicons.min.css [new file with mode: 0644]
LinuxGUI/WebUI/static/css/vtoy.css [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/ionicons.eot [new file with mode: 0644]
LinuxGUI/WebUI/static/fonts/ionicons.ttf [new file with mode: 0644]
LinuxGUI/WebUI/static/img/dropdown.png [new file with mode: 0644]
LinuxGUI/WebUI/static/img/refresh.ico [new file with mode: 0644]
LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js [new file with mode: 0644]
LinuxGUI/WebUI/static/js/jquery.validate.min.js [new file with mode: 0644]
LinuxGUI/WebUI/static/js/jquery.vtoy.alert.js [new file with mode: 0644]
LinuxGUI/WebUI/static/js/vtoy.js [new file with mode: 0644]
LinuxGUI/build.sh [new file with mode: 0644]
LinuxGUI/language.sh [new file with mode: 0644]

index 61f28d565a39ed1b4a412258363414d31ad51821..2535f490165736cd8b5933bf231b596a29c502cb 100644 (file)
@@ -17,6 +17,10 @@ Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX
 Please refer https://www.ventoy.net/en/doc_start.html for details.   
 
 
 Please refer https://www.ventoy.net/en/doc_start.html for details.   
 
 
+========== VentoyWeb.sh ===============
+sudo sh VentoyWeb.sh 
+Normally, it will popup a web browser window. 
+If not you can open your browser and visit http://127.0.0.1:24680
 
 
 
 
 
 
diff --git a/INSTALL/VentoyWeb.sh b/INSTALL/VentoyWeb.sh
new file mode 100644 (file)
index 0000000..3bc9469
--- /dev/null
@@ -0,0 +1,182 @@
+#!/bin/sh
+
+print_usage() {    
+    echo 'Usage:  VentoyWeb.sh [ OPTION ]'   
+    echo '  OPTION: (optional)'
+    echo '   -H x.x.x.x  http server IP address (default is 127.0.0.1)'
+    echo '   -p PORT     http server PORT (default is 24680)'
+    echo "   -n          don't start web browser"
+    echo '   -h          print this help'
+    echo ''
+}
+
+print_err() {
+    echo ""
+    echo "$*"
+    echo ""
+}
+
+check_option() {
+    app="$1"
+    $app --help 2>&1 | grep -q "$2"
+}
+
+get_user() {
+    name=$(logname)
+    if [ -n "$name" -a "$name" != "root" ]; then
+        echo $name; return
+    fi
+    
+    name=${HOME#/home/}
+    if [ -n "$name" -a "$name" != "root" ]; then
+        echo $name; return
+    fi
+}
+
+chromium_proc() {
+    app="$1"
+    
+    url="http://${HOST}:${PORT}/index.html"
+    
+    if check_option "$app" '[-][-]app='; then
+        su $VUSER -c "$app --app=$url >> $LOGFILE 2>&1"
+    elif check_option "$app" '[-][-]new[-]window='; then
+        su $VUSER -c "$app --new-window $url >> $LOGFILE 2>&1"
+    else
+        su $VUSER -c "$app $url >> $LOGFILE 2>&1"
+    fi
+}
+
+uid=$(id -u)
+if [ $uid -ne 0 ]; then
+    print_err "Please use sudo or run the script as root."
+    exit 1
+fi
+
+OLDDIR=$(pwd)
+
+if uname -a | egrep -q 'aarch64|arm64'; then
+    TOOLDIR=aarch64
+elif uname -a | egrep -q 'x86_64|amd64'; then
+    TOOLDIR=x86_64
+else
+    TOOLDIR=i386
+fi
+
+if [ ! -f ./tool/$TOOLDIR/V2DServer ]; then
+    if [ -f ${0%VentoyWeb.sh}/tool/$TOOLDIR/V2DServer ]; then
+        cd ${0%VentoyWeb.sh}
+    fi
+fi
+
+PATH=./tool/$TOOLDIR:$PATH
+
+if [ ! -f ./boot/boot.img ]; then
+    if [ -d ./grub ]; then
+        echo "Don't run VentoyWeb.sh here, please download the released install package, and run the script in it."
+    else
+        echo "Please run under the correct directory!" 
+    fi
+    exit 1
+fi
+
+HOST="127.0.0.1"
+PORT=24680
+
+while [ -n "$1" ]; do
+    if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+        print_usage
+        exit 0
+    elif [ "$1" = "-n" ]; then
+        NOWEB=1
+    elif [ "$1" = "-H" ]; then
+        shift
+        if echo $1 | grep -q '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*'; then
+            HOST="$1"
+        else
+            print_err "Invalid host $1"
+            exit 1
+        fi        
+    elif [ "$1" = "-p" ]; then
+        shift
+        if [ $1 -gt 0 -a $1 -le 65535 ]; then
+            PORT="$1"
+        else
+            print_err "Invalid port $1"
+            exit 1
+        fi
+    fi
+    
+    shift
+done
+
+
+if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
+    print_err "Another ventoy server is running now, please close it first."
+    exit 1
+fi
+
+VUSER=$(get_user)
+LOGFILE=log.txt
+#delete the log.txt if it's more than 8MB
+if [ -f $LOGFILE ]; then
+    logsize=$(stat -c '%s' $LOGFILE)
+    if [ $logsize -gt 8388608 ]; then
+        rm -f $LOGFILE
+        su $VUSER -c "touch $LOGFILE"
+    fi
+else
+    su $VUSER -c "touch $LOGFILE"
+fi
+
+
+
+if [ -f ./tool/$TOOLDIR/V2DServer.xz ]; then
+    xz -d ./tool/$TOOLDIR/V2DServer.xz
+    chmod +x ./tool/$TOOLDIR/V2DServer
+fi
+
+V2DServer "$HOST" "$PORT" &
+
+vtVer=$(cat ventoy/version)
+echo ""
+echo "=================================================================="
+echo "  Ventoy Server $vtVer is running at http://${HOST}:${PORT} ..."
+echo "=================================================================="
+echo ""
+echo "################ Press Ctrl + C to exit ######################"
+echo ""
+
+if [ "$NOWEB" = "1" ]; then
+    echo "Please open your web browser and visit http://${HOST}:${PORT}"
+else
+    if which -a google-chrome-stable >> $LOGFILE 2>&1; then    
+        chromium_proc google-chrome-stable
+    elif which -a google-chrome >> $LOGFILE 2>&1; then    
+        chromium_proc google-chrome
+    elif which -a chrome >> $LOGFILE 2>&1; then    
+        chromium_proc chrome
+    elif which -a browser >> $LOGFILE 2>&1; then        
+        chromium_proc browser        
+    elif which -a firefox >> $LOGFILE 2>&1; then
+        su $VUSER -c "firefox --no-remote \"http://${HOST}:${PORT}/index.html\""
+    else
+        echo "Please open your web browser and visit http://${HOST}:${PORT}"
+    fi
+fi
+
+if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
+    echo ""
+else
+    print_err "Ventoy Server Error! Please check log.txt."
+fi
+
+wait $!
+
+
+if [ -n "$OLDDIR" ]; then 
+    CURDIR=$(pwd)
+    if [ "$CURDIR" != "$OLDDIR" ]; then
+        cd "$OLDDIR"
+    fi
+fi
index e9cfe9e92420ea5323e890722fff552cc9a9dab3..8c2655c35103f0c0920b5a156fde1390d96e5a52 100644 (file)
@@ -25,6 +25,11 @@ sh mkcpio.sh
 sh mkloopex.sh
 cd -
 
 sh mkloopex.sh
 cd -
 
+cd ../LinuxGUI
+sh language.sh || exit 1
+sh build.sh
+cd -
+
 
 LOOP=$(losetup -f)
 
 
 LOOP=$(losetup -f)
 
@@ -88,12 +93,17 @@ xz --check=crc32 $tmpdir/boot/core.img
 cp $OPT ./tool $tmpdir/
 rm -f $tmpdir/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
 cp $OPT Ventoy2Disk.sh $tmpdir/
 cp $OPT ./tool $tmpdir/
 rm -f $tmpdir/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
 cp $OPT Ventoy2Disk.sh $tmpdir/
+cp $OPT VentoyWeb.sh $tmpdir/
 cp $OPT README $tmpdir/
 cp $OPT plugin $tmpdir/
 cp $OPT CreatePersistentImg.sh $tmpdir/
 dos2unix -q $tmpdir/Ventoy2Disk.sh
 cp $OPT README $tmpdir/
 cp $OPT plugin $tmpdir/
 cp $OPT CreatePersistentImg.sh $tmpdir/
 dos2unix -q $tmpdir/Ventoy2Disk.sh
+dos2unix -q $tmpdir/VentoyWeb.sh
 dos2unix -q $tmpdir/CreatePersistentImg.sh
 
 dos2unix -q $tmpdir/CreatePersistentImg.sh
 
+cp $OPT ../LinuxGUI/WebUI $tmpdir/
+sed 's/.*SCRIPT_DEL_THIS \(.*\)/\1/g' -i $tmpdir/WebUI/index.html
+
 #32MB disk img
 dd status=none if=$LOOP of=$tmpdir/ventoy/ventoy.disk.img bs=512 count=$VENTOY_SECTOR_NUM skip=$part2_start_sector
 xz --check=crc32 $tmpdir/ventoy/ventoy.disk.img
 #32MB disk img
 dd status=none if=$LOOP of=$tmpdir/ventoy/ventoy.disk.img bs=512 count=$VENTOY_SECTOR_NUM skip=$part2_start_sector
 xz --check=crc32 $tmpdir/ventoy/ventoy.disk.img
@@ -119,6 +129,7 @@ done
 find $tmpdir/ -type d -exec chmod 755 "{}" +
 find $tmpdir/ -type f -exec chmod 644 "{}" +
 chmod +x $tmpdir/Ventoy2Disk.sh
 find $tmpdir/ -type d -exec chmod 755 "{}" +
 find $tmpdir/ -type f -exec chmod 644 "{}" +
 chmod +x $tmpdir/Ventoy2Disk.sh
+chmod +x $tmpdir/VentoyWeb.sh
 chmod +x $tmpdir/CreatePersistentImg.sh
 
 tar -czvf ventoy-${curver}-linux.tar.gz $tmpdir
 chmod +x $tmpdir/CreatePersistentImg.sh
 
 tar -czvf ventoy-${curver}-linux.tar.gz $tmpdir
@@ -130,6 +141,7 @@ cp $OPT Ventoy2Disk*.exe $tmpdir/
 cp $OPT $LANG_DIR/languages.ini $tmpdir/ventoy/
 rm -rf $tmpdir/tool
 rm -f $tmpdir/*.sh
 cp $OPT $LANG_DIR/languages.ini $tmpdir/ventoy/
 rm -rf $tmpdir/tool
 rm -f $tmpdir/*.sh
+rm -rf $tmpdir/WebUI
 rm -f $tmpdir/README
 
 
 rm -f $tmpdir/README
 
 
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c
new file mode 100644 (file)
index 0000000..9f80c61
--- /dev/null
@@ -0,0 +1,299 @@
+/******************************************************************************
+ * crc32.c  ---- ventoy crc32
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+static uint32_t g_crc_table[256] = {
+  0x00000000,
+  0x77073096,
+  0xEE0E612C,
+  0x990951BA,
+  0x076DC419,
+  0x706AF48F,
+  0xE963A535,
+  0x9E6495A3,
+  0x0EDB8832,
+  0x79DCB8A4,
+  0xE0D5E91E,
+  0x97D2D988,
+  0x09B64C2B,
+  0x7EB17CBD,
+  0xE7B82D07,
+  0x90BF1D91,
+  0x1DB71064,
+  0x6AB020F2,
+  0xF3B97148,
+  0x84BE41DE,
+  0x1ADAD47D,
+  0x6DDDE4EB,
+  0xF4D4B551,
+  0x83D385C7,
+  0x136C9856,
+  0x646BA8C0,
+  0xFD62F97A,
+  0x8A65C9EC,
+  0x14015C4F,
+  0x63066CD9,
+  0xFA0F3D63,
+  0x8D080DF5,
+  0x3B6E20C8,
+  0x4C69105E,
+  0xD56041E4,
+  0xA2677172,
+  0x3C03E4D1,
+  0x4B04D447,
+  0xD20D85FD,
+  0xA50AB56B,
+  0x35B5A8FA,
+  0x42B2986C,
+  0xDBBBC9D6,
+  0xACBCF940,
+  0x32D86CE3,
+  0x45DF5C75,
+  0xDCD60DCF,
+  0xABD13D59,
+  0x26D930AC,
+  0x51DE003A,
+  0xC8D75180,
+  0xBFD06116,
+  0x21B4F4B5,
+  0x56B3C423,
+  0xCFBA9599,
+  0xB8BDA50F,
+  0x2802B89E,
+  0x5F058808,
+  0xC60CD9B2,
+  0xB10BE924,
+  0x2F6F7C87,
+  0x58684C11,
+  0xC1611DAB,
+  0xB6662D3D,
+  0x76DC4190,
+  0x01DB7106,
+  0x98D220BC,
+  0xEFD5102A,
+  0x71B18589,
+  0x06B6B51F,
+  0x9FBFE4A5,
+  0xE8B8D433,
+  0x7807C9A2,
+  0x0F00F934,
+  0x9609A88E,
+  0xE10E9818,
+  0x7F6A0DBB,
+  0x086D3D2D,
+  0x91646C97,
+  0xE6635C01,
+  0x6B6B51F4,
+  0x1C6C6162,
+  0x856530D8,
+  0xF262004E,
+  0x6C0695ED,
+  0x1B01A57B,
+  0x8208F4C1,
+  0xF50FC457,
+  0x65B0D9C6,
+  0x12B7E950,
+  0x8BBEB8EA,
+  0xFCB9887C,
+  0x62DD1DDF,
+  0x15DA2D49,
+  0x8CD37CF3,
+  0xFBD44C65,
+  0x4DB26158,
+  0x3AB551CE,
+  0xA3BC0074,
+  0xD4BB30E2,
+  0x4ADFA541,
+  0x3DD895D7,
+  0xA4D1C46D,
+  0xD3D6F4FB,
+  0x4369E96A,
+  0x346ED9FC,
+  0xAD678846,
+  0xDA60B8D0,
+  0x44042D73,
+  0x33031DE5,
+  0xAA0A4C5F,
+  0xDD0D7CC9,
+  0x5005713C,
+  0x270241AA,
+  0xBE0B1010,
+  0xC90C2086,
+  0x5768B525,
+  0x206F85B3,
+  0xB966D409,
+  0xCE61E49F,
+  0x5EDEF90E,
+  0x29D9C998,
+  0xB0D09822,
+  0xC7D7A8B4,
+  0x59B33D17,
+  0x2EB40D81,
+  0xB7BD5C3B,
+  0xC0BA6CAD,
+  0xEDB88320,
+  0x9ABFB3B6,
+  0x03B6E20C,
+  0x74B1D29A,
+  0xEAD54739,
+  0x9DD277AF,
+  0x04DB2615,
+  0x73DC1683,
+  0xE3630B12,
+  0x94643B84,
+  0x0D6D6A3E,
+  0x7A6A5AA8,
+  0xE40ECF0B,
+  0x9309FF9D,
+  0x0A00AE27,
+  0x7D079EB1,
+  0xF00F9344,
+  0x8708A3D2,
+  0x1E01F268,
+  0x6906C2FE,
+  0xF762575D,
+  0x806567CB,
+  0x196C3671,
+  0x6E6B06E7,
+  0xFED41B76,
+  0x89D32BE0,
+  0x10DA7A5A,
+  0x67DD4ACC,
+  0xF9B9DF6F,
+  0x8EBEEFF9,
+  0x17B7BE43,
+  0x60B08ED5,
+  0xD6D6A3E8,
+  0xA1D1937E,
+  0x38D8C2C4,
+  0x4FDFF252,
+  0xD1BB67F1,
+  0xA6BC5767,
+  0x3FB506DD,
+  0x48B2364B,
+  0xD80D2BDA,
+  0xAF0A1B4C,
+  0x36034AF6,
+  0x41047A60,
+  0xDF60EFC3,
+  0xA867DF55,
+  0x316E8EEF,
+  0x4669BE79,
+  0xCB61B38C,
+  0xBC66831A,
+  0x256FD2A0,
+  0x5268E236,
+  0xCC0C7795,
+  0xBB0B4703,
+  0x220216B9,
+  0x5505262F,
+  0xC5BA3BBE,
+  0xB2BD0B28,
+  0x2BB45A92,
+  0x5CB36A04,
+  0xC2D7FFA7,
+  0xB5D0CF31,
+  0x2CD99E8B,
+  0x5BDEAE1D,
+  0x9B64C2B0,
+  0xEC63F226,
+  0x756AA39C,
+  0x026D930A,
+  0x9C0906A9,
+  0xEB0E363F,
+  0x72076785,
+  0x05005713,
+  0x95BF4A82,
+  0xE2B87A14,
+  0x7BB12BAE,
+  0x0CB61B38,
+  0x92D28E9B,
+  0xE5D5BE0D,
+  0x7CDCEFB7,
+  0x0BDBDF21,
+  0x86D3D2D4,
+  0xF1D4E242,
+  0x68DDB3F8,
+  0x1FDA836E,
+  0x81BE16CD,
+  0xF6B9265B,
+  0x6FB077E1,
+  0x18B74777,
+  0x88085AE6,
+  0xFF0F6A70,
+  0x66063BCA,
+  0x11010B5C,
+  0x8F659EFF,
+  0xF862AE69,
+  0x616BFFD3,
+  0x166CCF45,
+  0xA00AE278,
+  0xD70DD2EE,
+  0x4E048354,
+  0x3903B3C2,
+  0xA7672661,
+  0xD06016F7,
+  0x4969474D,
+  0x3E6E77DB,
+  0xAED16A4A,
+  0xD9D65ADC,
+  0x40DF0B66,
+  0x37D83BF0,
+  0xA9BCAE53,
+  0xDEBB9EC5,
+  0x47B2CF7F,
+  0x30B5FFE9,
+  0xBDBDF21C,
+  0xCABAC28A,
+  0x53B39330,
+  0x24B4A3A6,
+  0xBAD03605,
+  0xCDD70693,
+  0x54DE5729,
+  0x23D967BF,
+  0xB3667A2E,
+  0xC4614AB8,
+  0x5D681B02,
+  0x2A6F2B94,
+  0xB40BBE37,
+  0xC30C8EA1,
+  0x5A05DF1B,
+  0x2D02EF8D
+};
+
+uint32_t ventoy_crc32(void *Buffer, uint32_t Length)
+{
+    uint32_t  i;
+    uint8_t  *Ptr = Buffer;
+    uint32_t  Crc = 0xFFFFFFFF;
+
+    for (i = 0; i < Length; i++, Ptr++) 
+    {
+        Crc = (Crc >> 8) ^ g_crc_table[(uint8_t) Crc ^ *Ptr];
+    }
+
+    return Crc ^ 0xffffffff;
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_define.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_define.h
new file mode 100644 (file)
index 0000000..14b2cf4
--- /dev/null
@@ -0,0 +1,192 @@
+/******************************************************************************
+ * ventoy_define.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_DEFINE_H__
+#define __VENTOY_DEFINE_H__
+
+#define MAX_DISK_NUM    256
+
+#define SIZE_1MB    1048576
+#define SIZE_1GB    1073741824
+
+#define VTOYIMG_PART_START_BYTES    (1024 * 1024)
+#define VTOYIMG_PART_START_SECTOR   2048
+
+#define VTOYEFI_PART_BYTES    (32 * 1024 * 1024)
+#define VTOYEFI_PART_SECTORS  65536
+
+#define VTOY_LOG_FILE "log.txt"
+
+
+#pragma pack(1)
+
+typedef struct vtoy_guid
+{
+    uint32_t   data1;
+    uint16_t   data2;
+    uint16_t   data3;
+    uint8_t    data4[8];
+}vtoy_guid;
+
+typedef struct PART_TABLE
+{
+    uint8_t  Active; // 0x00  0x80
+
+    uint8_t  StartHead;
+    uint16_t StartSector : 6;
+    uint16_t StartCylinder : 10;
+
+    uint8_t  FsFlag;
+
+    uint8_t  EndHead;
+    uint16_t EndSector : 6;
+    uint16_t EndCylinder : 10;
+
+    uint32_t StartSectorId;
+    uint32_t SectorCount;
+}PART_TABLE;
+
+typedef struct MBR_HEAD
+{
+    uint8_t BootCode[446];
+    PART_TABLE PartTbl[4];
+    uint8_t Byte55;
+    uint8_t ByteAA;
+}MBR_HEAD;
+
+typedef struct VTOY_GPT_HDR
+{
+    char     Signature[8]; /* EFI PART */
+    uint8_t  Version[4];
+    uint32_t Length;
+    uint32_t Crc;
+    uint8_t  Reserved1[4];
+    uint64_t EfiStartLBA;
+    uint64_t EfiBackupLBA;
+    uint64_t PartAreaStartLBA;
+    uint64_t PartAreaEndLBA;
+    vtoy_guid   DiskGuid;
+    uint64_t PartTblStartLBA;
+    uint32_t PartTblTotNum;
+    uint32_t PartTblEntryLen;
+    uint32_t PartTblCrc;
+    uint8_t  Reserved2[420];
+}VTOY_GPT_HDR;
+
+typedef struct VTOY_GPT_PART_TBL
+{
+    vtoy_guid   PartType;
+    vtoy_guid   PartGuid;
+    uint64_t StartLBA;
+    uint64_t LastLBA;
+    uint64_t Attr;
+    uint16_t Name[36];
+}VTOY_GPT_PART_TBL;
+
+typedef struct VTOY_GPT_INFO
+{
+    MBR_HEAD MBR;
+    VTOY_GPT_HDR Head;
+    VTOY_GPT_PART_TBL PartTbl[128];
+}VTOY_GPT_INFO;
+#pragma pack()
+
+
+#define MBR_PART_STYLE  0
+#define GPT_PART_STYLE  1
+
+typedef struct disk_ventoy_data
+{
+    int ventoy_valid;
+    
+    char ventoy_ver[32];  // 1.0.33 ...
+    int  secure_boot_flag;
+    uint64_t preserved_space;
+
+    uint64_t part2_start_sector;
+
+    int  partition_style; // MBR_PART_STYLE/GPT_PART_STYLE
+    VTOY_GPT_INFO gptinfo;
+    uint8_t rsvdata[4096];
+}disk_ventoy_data;
+
+
+typedef struct ventoy_disk
+{
+    char disk_name[32];   // sda
+    char disk_path[64];   // /dev/sda
+
+    char part1_name[32];   // sda1
+    char part1_path[64];   // /dev/sda1
+    char part2_name[32];   // sda2
+    char part2_path[64];   // /dev/sda2
+
+    char disk_model[256]; // Sandisk/Kingston ...
+    char human_readable_size[32];
+
+    int major;
+    int minor;
+    int type;
+    int partstyle;
+    uint64_t size_in_byte;
+
+    disk_ventoy_data vtoydata;
+}ventoy_disk;
+
+#pragma pack(1)
+typedef struct ventoy_guid
+{
+    uint32_t   data1;
+    uint16_t   data2;
+    uint16_t   data3;
+    uint8_t    data4[8];
+}ventoy_guid;
+#pragma pack()
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define VLOG_LOG    1
+#define VLOG_DEBUG  2
+
+#define ulong unsigned long
+#define _ll long long
+#define _ull unsigned long long
+#define strlcpy(dst, src) strncpy(dst, src, sizeof(dst) - 1)
+#define scnprintf(dst, fmt, args...) snprintf(dst, sizeof(dst) - 1, fmt, ##args)
+
+#define vlog(fmt, args...) ventoy_syslog(VLOG_LOG, fmt, ##args)
+#define vdebug(fmt, args...) ventoy_syslog(VLOG_DEBUG, fmt, ##args)
+
+void ventoy_syslog(int level, const char *Fmt, ...);
+void ventoy_set_loglevel(int level);
+uint32_t ventoy_crc32(void *Buffer, uint32_t Length);
+
+
+static inline void * zalloc(size_t n)
+{
+    void *p = malloc(n);
+    if (p) memset(p, 0, n);
+    return p;
+}
+
+
+#endif /* __VENTOY_DEFINE_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c
new file mode 100644 (file)
index 0000000..4d0871b
--- /dev/null
@@ -0,0 +1,735 @@
+/******************************************************************************
+ * ventoy_disk.c  ---- ventoy disk
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <time.h>
+#include <ventoy_define.h>
+#include <ventoy_disk.h>
+#include <ventoy_util.h>
+#include <fat_filelib.h>
+
+int g_disk_num = 0;
+static int g_fatlib_media_fd = 0;
+static uint64_t g_fatlib_media_offset = 0;
+ventoy_disk *g_disk_list = NULL;
+
+static const char *g_ventoy_dev_type_str[VTOY_DEVICE_END] = 
+{
+    "unknown", "scsi", "USB", "ide", "dac960",
+    "cpqarray", "file", "ataraid", "i2o",
+    "ubd", "dasd", "viodasd", "sx8", "dm",
+    "xvd", "sd/mmc", "virtblk", "aoe",
+    "md", "loopback", "nvme", "brd", "pmem"
+};
+
+static const char * ventoy_get_dev_type_name(ventoy_dev_type type)
+{
+    return (type < VTOY_DEVICE_END) ? g_ventoy_dev_type_str[type] : "unknown";
+}
+
+static int ventoy_check_blk_major(int major, const char *type)
+{
+    int flag = 0;
+    int valid = 0;
+    int devnum = 0;
+    int len = 0;
+    char line[64];
+    char *pos = NULL;
+    FILE *fp = NULL;
+
+    fp = fopen("/proc/devices", "r");
+    if (!fp)
+    {
+        return 0;
+    }
+
+    len = (int)strlen(type);
+    while (fgets(line, sizeof(line), fp))
+    {
+        if (flag)
+        {
+            pos = strchr(line, ' ');
+            if (pos)
+            {
+                devnum = (int)strtol(line, NULL, 10);
+                if (devnum == major)
+                {
+                    if (strncmp(pos + 1, type, len) == 0)
+                    {
+                        valid = 1;                        
+                    }
+                    break;
+                }
+            }
+        }
+        else if (strncmp(line, "Block devices:", 14) == 0)
+        {
+            flag = 1;
+        }
+    }
+
+    fclose(fp);
+    return valid;
+}
+
+static int ventoy_get_disk_devnum(const char *name, int *major, int* minor)
+{
+    int rc;
+    char *pos;
+    char devnum[16] = {0};
+    
+    rc = ventoy_get_sys_file_line(devnum, sizeof(devnum), "/sys/block/%s/dev", name);
+    if (rc)
+    {
+        return 1;
+    }
+
+    pos = strstr(devnum, ":");
+    if (!pos)
+    {
+        return 1;
+    }
+
+    *major = (int)strtol(devnum, NULL, 10);
+    *minor = (int)strtol(pos + 1, NULL, 10);
+
+    return 0;
+}
+
+static ventoy_dev_type ventoy_get_dev_type(const char *name, int major, int minor)
+{
+    int rc;
+    char syspath[128];
+    char dstpath[256];
+
+    memset(syspath, 0, sizeof(syspath));
+    memset(dstpath, 0, sizeof(dstpath));
+    
+    scnprintf(syspath, "/sys/block/%s", name);
+    rc = readlink(syspath, dstpath, sizeof(dstpath) - 1);
+    if (rc > 0 && strstr(dstpath, "/usb"))
+    {
+        return VTOY_DEVICE_USB;
+    }
+    
+    if (SCSI_BLK_MAJOR(major) && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_SCSI;
+    }
+    else if (IDE_BLK_MAJOR(major) && (minor % 0x40 == 0)) 
+    {
+        return VTOY_DEVICE_IDE;
+    }
+    else if (major == DAC960_MAJOR && (minor % 0x8 == 0)) 
+    {
+        return VTOY_DEVICE_DAC960;
+    }
+    else if (major == ATARAID_MAJOR && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_ATARAID;
+    }
+    else if (major == AOE_MAJOR && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_AOE;
+    }
+    else if (major == DASD_MAJOR && (minor % 0x4 == 0)) 
+    {
+        return VTOY_DEVICE_DASD;
+    }
+    else if (major == VIODASD_MAJOR && (minor % 0x8 == 0)) 
+    {
+        return VTOY_DEVICE_VIODASD;
+    }
+    else if (SX8_BLK_MAJOR(major) && (minor % 0x20 == 0)) 
+    {
+        return VTOY_DEVICE_SX8;
+    }
+    else if (I2O_BLK_MAJOR(major) && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_I2O;
+    }
+    else if (CPQARRAY_BLK_MAJOR(major) && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_CPQARRAY;
+    }
+    else if (UBD_MAJOR == major && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_UBD;
+    }
+    else if (XVD_MAJOR == major && (minor % 0x10 == 0)) 
+    {
+        return VTOY_DEVICE_XVD;
+    }
+    else if (SDMMC_MAJOR == major && (minor % 0x8 == 0)) 
+    {
+        return VTOY_DEVICE_SDMMC;
+    }
+    else if (ventoy_check_blk_major(major, "virtblk"))
+    {
+        return VTOY_DEVICE_VIRTBLK;
+    }
+    else if (major == LOOP_MAJOR)
+    {
+        return VTOY_DEVICE_LOOP;
+    }
+    else if (major == MD_MAJOR)
+    {
+        return VTOY_DEVICE_MD;
+    }
+    else if (major == RAM_MAJOR)
+    {
+        return VTOY_DEVICE_RAM;
+    }
+    else if (strstr(name, "nvme") && ventoy_check_blk_major(major, "blkext"))
+    {
+        return VTOY_DEVICE_NVME;
+    }
+    else if (strstr(name, "pmem") && ventoy_check_blk_major(major, "blkext"))
+    {
+        return VTOY_DEVICE_PMEM;
+    }
+    
+    return VTOY_DEVICE_END;
+}
+
+static int ventoy_is_possible_blkdev(const char *name)
+{
+    if (name[0] == '.')
+    {
+        return 0;
+    }
+
+    /* /dev/ramX */
+    if (name[0] == 'r' && name[1] == 'a' && name[2] == 'm')
+    {
+        return 0;
+    }
+
+    /* /dev/loopX */
+    if (name[0] == 'l' && name[1] == 'o' && name[2] == 'o' && name[3] == 'p')
+    {
+        return 0;
+    }
+
+    /* /dev/dm-X */
+    if (name[0] == 'd' && name[1] == 'm' && name[2] == '-' && isdigit(name[3]))
+    {
+        return 0;
+    }
+
+    /* /dev/srX */
+    if (name[0] == 's' && name[1] == 'r' && isdigit(name[2]))
+    {
+        return 0;
+    }
+    
+    return 1;
+}
+
+uint64_t ventoy_get_disk_size_in_byte(const char *disk)
+{
+    int fd;
+    int rc;
+    unsigned long long size = 0;
+    char diskpath[256] = {0};
+    char sizebuf[64] = {0};
+
+    // Try 1: get size from sysfs
+    snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", disk);
+    if (access(diskpath, F_OK) >= 0)
+    {
+        vdebug("get disk size from sysfs for %s\n", disk);
+        
+        fd = open(diskpath, O_RDONLY | O_BINARY);
+        if (fd >= 0)
+        {
+            read(fd, sizebuf, sizeof(sizebuf));
+            size = strtoull(sizebuf, NULL, 10);
+            close(fd);
+            return (uint64_t)(size * 512);
+        }
+    }
+    else
+    {
+        vdebug("%s not exist \n", diskpath);
+    }
+
+    // Try 2: get size from ioctl
+    snprintf(diskpath, sizeof(diskpath) - 1, "/dev/%s", disk);
+    fd = open(diskpath, O_RDONLY);
+    if (fd >= 0)
+    {
+        vdebug("get disk size from ioctl for %s\n", disk);
+        rc = ioctl(fd, BLKGETSIZE64, &size);
+        if (rc == -1)
+        {
+            size = 0;
+            vdebug("failed to ioctl %d\n", rc);
+        }
+        close(fd);
+    }
+    else
+    {
+        vdebug("failed to open %s %d\n", diskpath, errno);
+    }
+
+    vdebug("disk %s size %llu bytes\n", disk, size);
+    return size;
+}
+
+int ventoy_get_disk_vendor(const char *name, char *vendorbuf, int bufsize)
+{
+    return ventoy_get_sys_file_line(vendorbuf, bufsize, "/sys/block/%s/device/vendor", name);
+}
+
+int ventoy_get_disk_model(const char *name, char *modelbuf, int bufsize)
+{
+    return ventoy_get_sys_file_line(modelbuf, bufsize, "/sys/block/%s/device/model", name);
+}
+
+static int fatlib_media_sector_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+    lseek(g_fatlib_media_fd, (sector + g_fatlib_media_offset) * 512ULL, SEEK_SET);
+    read(g_fatlib_media_fd, buffer, sector_count * 512);
+    
+    return 1;
+}
+
+static int fatlib_is_secure_boot_enable(void)
+{
+    void *flfile = NULL;
+    
+    flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+    if (flfile)
+    {
+        fl_fclose(flfile);
+        return 1;
+    }
+    else
+    {
+        vlog("/EFI/BOOT/grubx64_real.efi not exist\n");
+    }
+    
+    return 0;
+}
+
+static int fatlib_get_ventoy_version(char *verbuf, int bufsize)
+{
+    int rc = 1;
+    int size = 0;
+    char *buf = NULL;
+    char *pos = NULL;
+    char *end = NULL;
+    void *flfile = NULL;
+
+    flfile = fl_fopen("/grub/grub.cfg", "rb");
+    if (flfile)
+    {
+        fl_fseek(flfile, 0, SEEK_END);
+        size = (int)fl_ftell(flfile);
+
+        fl_fseek(flfile, 0, SEEK_SET);
+
+        buf = malloc(size + 1);
+        if (buf)
+        {
+            fl_fread(buf, 1, size, flfile);
+            buf[size] = 0;
+
+            pos = strstr(buf, "VENTOY_VERSION=");
+            if (pos)
+            {
+                pos += strlen("VENTOY_VERSION=");
+                if (*pos == '"')
+                {
+                    pos++;
+                }
+
+                end = pos;
+                while (*end != 0 && *end != '"' && *end != '\r' && *end != '\n')
+                {
+                    end++;
+                }
+
+                *end = 0;
+
+                snprintf(verbuf, bufsize - 1, "%s", pos);
+                rc = 0;
+            }
+            free(buf);
+        }
+
+        fl_fclose(flfile);
+    }
+    else
+    {
+        vdebug("No grub.cfg found\n");
+    }
+    
+    return rc;
+}
+
+int ventoy_get_vtoy_data(ventoy_disk *info, int *ppartstyle)
+{
+    int i;
+    int fd;
+    int len;
+    int rc = 1;
+    int ret = 1;
+    int part_style;
+    uint64_t part1_start_sector;
+    uint64_t part1_sector_count;
+    uint64_t part2_start_sector;
+    uint64_t part2_sector_count;
+    uint64_t preserved_space;
+    char name[64] = {0};
+    disk_ventoy_data *vtoy = NULL;    
+    VTOY_GPT_INFO *gpt = NULL;
+    
+    vtoy = &(info->vtoydata);
+    gpt = &(vtoy->gptinfo);
+    memset(vtoy, 0, sizeof(disk_ventoy_data));
+
+    vdebug("ventoy_get_vtoy_data %s\n", info->disk_path);
+
+    if (info->size_in_byte < (2 * VTOYEFI_PART_BYTES))
+    {
+        vdebug("disk %s is too small %llu\n", info->disk_path, (_ull)info->size_in_byte);
+        return 1;
+    }
+
+    fd = open(info->disk_path, O_RDONLY | O_BINARY);
+    if (fd < 0)
+    {
+        vdebug("failed to open %s %d\n", info->disk_path, errno);
+        return 1;
+    }
+
+    len = (int)read(fd, &(vtoy->gptinfo), sizeof(VTOY_GPT_INFO));
+    if (len != sizeof(VTOY_GPT_INFO))
+    {
+        vdebug("failed to read %s %d\n", info->disk_path, errno);
+        goto end;
+    }
+
+    if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
+    {
+        vdebug("Invalid mbr magic 0x%x 0x%x\n", gpt->MBR.Byte55, gpt->MBR.ByteAA);
+        goto end;
+    }
+
+    if (gpt->MBR.PartTbl[0].FsFlag == 0xEE && strncmp(gpt->Head.Signature, "EFI PART", 8) == 0)
+    {
+        part_style = GPT_PART_STYLE;
+        if (ppartstyle)
+        {
+            *ppartstyle = part_style;
+        }
+
+        if (gpt->PartTbl[0].StartLBA == 0 || gpt->PartTbl[1].StartLBA == 0)
+        {
+            vdebug("NO ventoy efi part layout <%llu %llu>\n", 
+                (_ull)gpt->PartTbl[0].StartLBA,
+                (_ull)gpt->PartTbl[1].StartLBA);
+            goto end;
+        }
+
+        for (i = 0; i < 36; i++)
+        {
+            name[i] = (char)(gpt->PartTbl[1].Name[i]);
+        }
+        if (strcmp(name, "VTOYEFI"))
+        {
+            vdebug("Invalid efi part2 name <%s>\n", name);
+            goto end;
+        }
+
+        part1_start_sector = gpt->PartTbl[0].StartLBA;
+        part1_sector_count = gpt->PartTbl[0].LastLBA - part1_start_sector + 1;
+        part2_start_sector = gpt->PartTbl[1].StartLBA;
+        part2_sector_count = gpt->PartTbl[1].LastLBA - part2_start_sector + 1;
+
+        preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count + 33) * 512;
+    }
+    else
+    {
+        part_style = MBR_PART_STYLE;
+        if (ppartstyle)
+        {
+            *ppartstyle = part_style;
+        }
+        
+        part1_start_sector = gpt->MBR.PartTbl[0].StartSectorId;
+        part1_sector_count = gpt->MBR.PartTbl[0].SectorCount;
+        part2_start_sector = gpt->MBR.PartTbl[1].StartSectorId;
+        part2_sector_count = gpt->MBR.PartTbl[1].SectorCount;
+
+        preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count) * 512;
+    }
+
+    if (part1_start_sector != VTOYIMG_PART_START_SECTOR ||
+        part2_sector_count != VTOYEFI_PART_SECTORS ||
+        (part1_start_sector + part1_sector_count) != part2_start_sector)
+    {
+        vdebug("Not valid ventoy partition layout [%llu %llu] [%llu %llu]\n", 
+               part1_start_sector, part1_sector_count, part2_start_sector, part2_sector_count);
+        goto end;
+    }
+
+    vdebug("now check secure boot ...\n");
+
+    g_fatlib_media_fd = fd;
+    g_fatlib_media_offset = part2_start_sector;
+    fl_init();
+
+    if (0 == fl_attach_media(fatlib_media_sector_read, NULL))
+    {
+        ret = fatlib_get_ventoy_version(vtoy->ventoy_ver, sizeof(vtoy->ventoy_ver));
+        if (ret == 0 && vtoy->ventoy_ver[0])
+        {
+            vtoy->secure_boot_flag = fatlib_is_secure_boot_enable();            
+            vtoy->ventoy_valid = 1;
+        }
+        else
+        {
+            vdebug("fatlib_get_ventoy_version failed %d\n", ret);
+        }
+    }
+    else
+    {
+        vdebug("fl_attach_media failed\n");
+    }
+
+    fl_shutdown();
+    g_fatlib_media_fd = -1;
+    g_fatlib_media_offset = 0;
+
+    if (0 == vtoy->ventoy_valid)
+    {
+        goto end;
+    }
+    
+    lseek(fd, 2040 * 512, SEEK_SET);
+    read(fd, vtoy->rsvdata, sizeof(vtoy->rsvdata));
+
+    vtoy->preserved_space = preserved_space;
+    vtoy->partition_style = part_style;
+    vtoy->part2_start_sector = part2_start_sector;
+
+    rc = 0;
+end:
+    close(fd);
+    return rc;
+}
+
+int ventoy_get_disk_info(const char *name, ventoy_disk *info)
+{
+    char vendor[64] = {0};
+    char model[128] = {0};
+
+    vdebug("get disk info %s\n", name);
+
+    strlcpy(info->disk_name, name);
+    scnprintf(info->disk_path, "/dev/%s", name);
+
+    if (strstr(name, "nvme") || strstr(name, "mmc") || strstr(name, "nbd"))
+    {
+        scnprintf(info->part1_name, "%sp1", name);
+        scnprintf(info->part1_path, "/dev/%sp1", name);
+        scnprintf(info->part2_name, "%sp2", name);
+        scnprintf(info->part2_path, "/dev/%sp2", name);
+    }
+    else
+    {
+        scnprintf(info->part1_name, "%s1", name);
+        scnprintf(info->part1_path, "/dev/%s1", name);
+        scnprintf(info->part2_name, "%s2", name);
+        scnprintf(info->part2_path, "/dev/%s2", name);
+    }
+    
+    info->size_in_byte = ventoy_get_disk_size_in_byte(name);
+
+    ventoy_get_disk_devnum(name, &info->major, &info->minor);
+    info->type = ventoy_get_dev_type(name, info->major, info->minor);
+    ventoy_get_disk_vendor(name, vendor, sizeof(vendor));
+    ventoy_get_disk_model(name, model, sizeof(model));
+
+    scnprintf(info->human_readable_size, "%llu GB", (_ull)ventoy_get_human_readable_gb(info->size_in_byte));
+    scnprintf(info->disk_model, "%s %s (%s)", vendor, model, ventoy_get_dev_type_name(info->type));
+
+    ventoy_get_vtoy_data(info, &(info->partstyle));
+    
+    vdebug("disk:<%s %d:%d> model:<%s> size:%llu (%s)\n", 
+        info->disk_path, info->major, info->minor, info->disk_model, info->size_in_byte, info->human_readable_size);
+
+    if (info->vtoydata.ventoy_valid)
+    {
+        vdebug("%s Ventoy:<%s> %s secureboot:%d preserve:%llu\n", info->disk_path, info->vtoydata.ventoy_ver, 
+            info->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
+            info->vtoydata.secure_boot_flag, (_ull)(info->vtoydata.preserved_space));
+    }
+    else
+    {
+        vdebug("%s NO Ventoy detected\n", info->disk_path);
+    }
+
+    return 0;
+}
+
+static int ventoy_disk_compare(const ventoy_disk *disk1, const ventoy_disk *disk2)
+{
+    if (disk1->type == VTOY_DEVICE_USB && disk2->type == VTOY_DEVICE_USB)
+    {
+        return strcmp(disk1->disk_name, disk2->disk_name);
+    }
+    else if (disk1->type == VTOY_DEVICE_USB)
+    {
+        return -1;
+    }
+    else if (disk2->type == VTOY_DEVICE_USB)
+    {
+        return 1;
+    }
+    else
+    {
+        return strcmp(disk1->disk_name, disk2->disk_name);
+    }
+}
+
+static int ventoy_disk_sort(void)
+{
+    int i, j;
+    ventoy_disk *tmp;
+
+    tmp = malloc(sizeof(ventoy_disk));
+    if (!tmp)
+    {
+        return 1;
+    }
+
+    for (i = 0; i < g_disk_num; i++)
+    for (j = i + 1; j < g_disk_num; j++)
+    {
+        if (ventoy_disk_compare(g_disk_list + i, g_disk_list + j) > 0)
+        {
+            memcpy(tmp, g_disk_list + i, sizeof(ventoy_disk));
+            memcpy(g_disk_list + i, g_disk_list + j, sizeof(ventoy_disk));
+            memcpy(g_disk_list + j, tmp, sizeof(ventoy_disk));
+        }
+    }
+
+    free(tmp);
+    return 0;
+}
+
+int ventoy_disk_enumerate_all(void)
+{
+    int rc = 0;
+    DIR* dir = NULL;
+    struct dirent* p = NULL;
+
+    vdebug("ventoy_disk_enumerate_all\n");
+
+    dir = opendir("/sys/block");
+    if (!dir)
+    {
+        vlog("Failed to open /sys/block %d\n", errno);
+        return 1;
+    }
+
+    while (((p = readdir(dir)) != NULL) && (g_disk_num < MAX_DISK_NUM))
+    {
+        if (ventoy_is_possible_blkdev(p->d_name))
+        {
+            memset(g_disk_list + g_disk_num, 0, sizeof(ventoy_disk));
+            if (0 == ventoy_get_disk_info(p->d_name, g_disk_list + g_disk_num))
+            {
+                g_disk_num++;                    
+            }
+        }
+    }
+    closedir(dir);
+
+    ventoy_disk_sort();
+    
+    return rc;
+}
+
+void ventoy_disk_dump(ventoy_disk *cur)
+{
+    if (cur->vtoydata.ventoy_valid)
+    {
+        vdebug("%s [%s] %s\tVentoy: %s %s secureboot:%d preserve:%llu\n", 
+            cur->disk_path, cur->human_readable_size, cur->disk_model,
+            cur->vtoydata.ventoy_ver, cur->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
+            cur->vtoydata.secure_boot_flag, (_ull)(cur->vtoydata.preserved_space));
+    }
+    else
+    {
+        vdebug("%s [%s] %s\tVentoy: NA\n", cur->disk_path, cur->human_readable_size, cur->disk_model); 
+    }
+}
+
+void ventoy_disk_dump_all(void)
+{
+    int i;
+    
+    vdebug("============= DISK DUMP ============\n");
+    for (i = 0; i < g_disk_num; i++)
+    {
+        ventoy_disk_dump(g_disk_list + i);
+    }
+}
+
+int ventoy_disk_install(ventoy_disk *disk, void *efipartimg)
+{
+    return 0;
+}
+
+
+int ventoy_disk_init(void)
+{
+    g_disk_list = malloc(sizeof(ventoy_disk) * MAX_DISK_NUM);
+
+    ventoy_disk_enumerate_all();
+    ventoy_disk_dump_all();
+    
+    return 0;
+}
+
+void ventoy_disk_exit(void)
+{
+    check_free(g_disk_list);        
+    g_disk_list = NULL;
+    g_disk_num  = 0;
+}
+
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h
new file mode 100644 (file)
index 0000000..50618dc
--- /dev/null
@@ -0,0 +1,145 @@
+/******************************************************************************
+ * ventoy_disk.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_DISK_H__
+#define __VENTOY_DISK_H__
+
+typedef enum 
+{
+    VTOY_DEVICE_UNKNOWN = 0,
+    VTOY_DEVICE_SCSI,
+    VTOY_DEVICE_USB,
+    VTOY_DEVICE_IDE,
+    VTOY_DEVICE_DAC960,
+    VTOY_DEVICE_CPQARRAY,
+    VTOY_DEVICE_FILE,
+    VTOY_DEVICE_ATARAID,
+    VTOY_DEVICE_I2O,
+    VTOY_DEVICE_UBD,
+    VTOY_DEVICE_DASD,
+    VTOY_DEVICE_VIODASD,
+    VTOY_DEVICE_SX8,
+    VTOY_DEVICE_DM,
+    VTOY_DEVICE_XVD,
+    VTOY_DEVICE_SDMMC,
+    VTOY_DEVICE_VIRTBLK,
+    VTOY_DEVICE_AOE,
+    VTOY_DEVICE_MD,
+    VTOY_DEVICE_LOOP,
+    VTOY_DEVICE_NVME,
+    VTOY_DEVICE_RAM,
+    VTOY_DEVICE_PMEM,
+    
+    VTOY_DEVICE_END
+}ventoy_dev_type;
+
+/* from <linux/major.h> */
+#define IDE0_MAJOR              3
+#define IDE1_MAJOR              22
+#define IDE2_MAJOR              33
+#define IDE3_MAJOR              34
+#define IDE4_MAJOR              56
+#define IDE5_MAJOR              57
+#define SCSI_CDROM_MAJOR        11
+#define SCSI_DISK0_MAJOR        8
+#define SCSI_DISK1_MAJOR        65
+#define SCSI_DISK2_MAJOR        66
+#define SCSI_DISK3_MAJOR        67
+#define SCSI_DISK4_MAJOR        68
+#define SCSI_DISK5_MAJOR        69
+#define SCSI_DISK6_MAJOR        70
+#define SCSI_DISK7_MAJOR        71
+#define SCSI_DISK8_MAJOR        128
+#define SCSI_DISK9_MAJOR        129
+#define SCSI_DISK10_MAJOR       130
+#define SCSI_DISK11_MAJOR       131
+#define SCSI_DISK12_MAJOR       132
+#define SCSI_DISK13_MAJOR       133
+#define SCSI_DISK14_MAJOR       134
+#define SCSI_DISK15_MAJOR       135
+#define COMPAQ_SMART2_MAJOR     72
+#define COMPAQ_SMART2_MAJOR1    73
+#define COMPAQ_SMART2_MAJOR2    74
+#define COMPAQ_SMART2_MAJOR3    75
+#define COMPAQ_SMART2_MAJOR4    76
+#define COMPAQ_SMART2_MAJOR5    77
+#define COMPAQ_SMART2_MAJOR6    78
+#define COMPAQ_SMART2_MAJOR7    79
+#define COMPAQ_SMART_MAJOR      104
+#define COMPAQ_SMART_MAJOR1     105
+#define COMPAQ_SMART_MAJOR2     106
+#define COMPAQ_SMART_MAJOR3     107
+#define COMPAQ_SMART_MAJOR4     108
+#define COMPAQ_SMART_MAJOR5     109
+#define COMPAQ_SMART_MAJOR6     110
+#define COMPAQ_SMART_MAJOR7     111
+#define DAC960_MAJOR            48
+#define ATARAID_MAJOR           114
+#define I2O_MAJOR1              80
+#define I2O_MAJOR2              81
+#define I2O_MAJOR3              82
+#define I2O_MAJOR4              83
+#define I2O_MAJOR5              84
+#define I2O_MAJOR6              85
+#define I2O_MAJOR7              86
+#define I2O_MAJOR8              87
+#define UBD_MAJOR               98
+#define DASD_MAJOR              94
+#define VIODASD_MAJOR           112
+#define AOE_MAJOR               152
+#define SX8_MAJOR1              160
+#define SX8_MAJOR2              161
+#define XVD_MAJOR               202
+#define SDMMC_MAJOR             179
+#define LOOP_MAJOR              7
+#define MD_MAJOR                9
+#define BLKEXT_MAJOR            259
+#define RAM_MAJOR               1
+
+#define SCSI_BLK_MAJOR(M) (                                             \
+                (M) == SCSI_DISK0_MAJOR                                 \
+                || (M) == SCSI_CDROM_MAJOR                              \
+                || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \
+                || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
+
+#define IDE_BLK_MAJOR(M) \
+    ((M) == IDE0_MAJOR || \
+    (M) == IDE1_MAJOR || \
+    (M) == IDE2_MAJOR || \
+    (M) == IDE3_MAJOR || \
+    (M) == IDE4_MAJOR || \
+    (M) == IDE5_MAJOR)
+
+#define SX8_BLK_MAJOR(M) ((M) >= SX8_MAJOR1 && (M) <= SX8_MAJOR2)
+#define I2O_BLK_MAJOR(M) ((M) >= I2O_MAJOR1 && (M) <= I2O_MAJOR8)
+#define CPQARRAY_BLK_MAJOR(M)  \
+    (((M) >= COMPAQ_SMART2_MAJOR && (M) <= COMPAQ_SMART2_MAJOR7) || \
+    (COMPAQ_SMART_MAJOR <= (M) && (M) <= COMPAQ_SMART_MAJOR7))
+
+#define VENTOY_FILE_STG1_IMG    "boot/core.img.xz"
+#define VENTOY_FILE_DISK_IMG    "ventoy/ventoy.disk.img.xz"
+
+extern int g_disk_num;
+extern ventoy_disk *g_disk_list;
+int ventoy_disk_enumerate_all(void);
+int ventoy_disk_init(void);
+void ventoy_disk_exit(void);
+
+#endif /* __VENTOY_DISK_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_json.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.c
new file mode 100644 (file)
index 0000000..f65a458
--- /dev/null
@@ -0,0 +1,716 @@
+/******************************************************************************\r
+ * ventoy_json.c\r
+ *\r
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License as\r
+ * published by the Free Software Foundation; either version 3 of the\r
+ * License, or (at your option) any later version.\r
+ * \r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdint.h>\r
+#include <string.h>\r
+#include <stdarg.h>\r
+#include <errno.h>\r
+#include <unistd.h>\r
+#include <ventoy_define.h>\r
+#include <ventoy_util.h>\r
+#include <ventoy_json.h>\r
+\r
+static void vtoy_json_free(VTOY_JSON *pstJsonHead)\r
+{\r
+    VTOY_JSON *pstNext = NULL;\r
+\r
+    while (NULL != pstJsonHead)\r
+    {\r
+        pstNext = pstJsonHead->pstNext;\r
+        if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))\r
+        {\r
+            vtoy_json_free(pstJsonHead->pstChild);\r
+        }\r
+\r
+        free(pstJsonHead);\r
+        pstJsonHead = pstNext;\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+static char *vtoy_json_skip(const char *pcData)\r
+{\r
+    while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))\r
+    {\r
+        pcData++;\r
+    }\r
+\r
+    return (char *)pcData;\r
+}\r
+\r
+VTOY_JSON *vtoy_json_find_item\r
+(\r
+    VTOY_JSON *pstJson,\r
+    JSON_TYPE  enDataType,\r
+    const char *szKey\r
+)\r
+{\r
+    while (NULL != pstJson)\r
+    {\r
+        if ((enDataType == pstJson->enDataType) && \r
+            (0 == strcmp(szKey, pstJson->pcName)))\r
+        {\r
+            return pstJson;\r
+        }\r
+        pstJson = pstJson->pstNext;\r
+    }\r
+    \r
+    return NULL;\r
+}\r
+\r
+static int vtoy_json_parse_number\r
+(\r
+    VTOY_JSON *pstJson, \r
+    const char *pcData,\r
+    const char **ppcEnd\r
+)\r
+{\r
+    unsigned long Value;\r
+\r
+    Value = strtoul(pcData, (char **)ppcEnd, 10);\r
+    if (*ppcEnd == pcData)\r
+    {\r
+        vdebug("Failed to parse json number %s.\n", pcData);\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pstJson->enDataType = JSON_TYPE_NUMBER;\r
+    pstJson->unData.lValue = Value;\r
+    \r
+    return JSON_SUCCESS;\r
+}\r
+\r
+static int vtoy_json_parse_string\r
+(\r
+    char *pcNewStart,\r
+    char *pcRawStart,\r
+    VTOY_JSON *pstJson, \r
+    const char *pcData,\r
+    const char **ppcEnd\r
+)\r
+{\r
+    uint32_t uiLen = 0;\r
+    const char *pcPos = NULL;\r
+    const char *pcTmp = pcData + 1;\r
+    \r
+    *ppcEnd = pcData;\r
+\r
+    if ('\"' != *pcData)\r
+    {\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pcPos = strchr(pcTmp, '\"');\r
+    if ((NULL == pcPos) || (pcPos < pcTmp))\r
+    {\r
+        vdebug("Invalid string %s.\n", pcData);\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    *ppcEnd = pcPos + 1;\r
+    uiLen = (uint32_t)(unsigned long)(pcPos - pcTmp);    \r
+    \r
+    pstJson->enDataType = JSON_TYPE_STRING;\r
+    pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);\r
+    pstJson->unData.pcStrVal[uiLen] = '\0';\r
+    \r
+    return JSON_SUCCESS;\r
+}\r
+\r
+static int vtoy_json_parse_array\r
+(\r
+    char *pcNewStart,\r
+    char *pcRawStart,\r
+    VTOY_JSON *pstJson, \r
+    const char *pcData,\r
+    const char **ppcEnd\r
+)\r
+{\r
+    int Ret = JSON_SUCCESS;\r
+    VTOY_JSON *pstJsonChild = NULL;\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    const char *pcTmp = pcData + 1;\r
+\r
+    *ppcEnd = pcData;\r
+    pstJson->enDataType = JSON_TYPE_ARRAY;\r
+\r
+    if ('[' != *pcData)\r
+    {\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pcTmp = vtoy_json_skip(pcTmp);\r
+\r
+    if (']' == *pcTmp)\r
+    {\r
+        *ppcEnd = pcTmp + 1;\r
+        return JSON_SUCCESS;\r
+    }\r
+\r
+    JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);\r
+\r
+    Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);\r
+    if (JSON_SUCCESS != Ret)\r
+    {\r
+        vdebug("Failed to parse array child.\n");\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pstJsonChild = pstJson->pstChild;\r
+    pcTmp = vtoy_json_skip(*ppcEnd);\r
+    while ((NULL != pcTmp) && (',' == *pcTmp))\r
+    {\r
+        JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);\r
+        pstJsonChild->pstNext = pstJsonItem;\r
+        pstJsonItem->pstPrev = pstJsonChild;\r
+        pstJsonChild = pstJsonItem;\r
+\r
+        Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+        if (JSON_SUCCESS != Ret)\r
+        {\r
+            vdebug("Failed to parse array child.\n");\r
+            return JSON_FAILED;\r
+        }\r
+        pcTmp = vtoy_json_skip(*ppcEnd);\r
+    }\r
+\r
+    if ((NULL != pcTmp) && (']' == *pcTmp))\r
+    {\r
+        *ppcEnd = pcTmp + 1;\r
+        return JSON_SUCCESS;\r
+    }\r
+    else\r
+    {\r
+        *ppcEnd = pcTmp;\r
+        return JSON_FAILED;\r
+    }\r
+}\r
+\r
+static int vtoy_json_parse_object\r
+(\r
+    char *pcNewStart,\r
+    char *pcRawStart,\r
+    VTOY_JSON *pstJson, \r
+    const char *pcData,\r
+    const char **ppcEnd\r
+)\r
+{\r
+    int Ret = JSON_SUCCESS;\r
+    VTOY_JSON *pstJsonChild = NULL;\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    const char *pcTmp = pcData + 1;\r
+\r
+    *ppcEnd = pcData;\r
+    pstJson->enDataType = JSON_TYPE_OBJECT;\r
+\r
+    if ('{' != *pcData)\r
+    {\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pcTmp = vtoy_json_skip(pcTmp);\r
+    if ('}' == *pcTmp)\r
+    {\r
+        *ppcEnd = pcTmp + 1;\r
+        return JSON_SUCCESS;\r
+    }\r
+\r
+    JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);\r
+\r
+    Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);\r
+    if (JSON_SUCCESS != Ret)\r
+    {\r
+        vdebug("Failed to parse array child.\n");\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pstJsonChild = pstJson->pstChild;\r
+    pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;\r
+    pstJsonChild->unData.pcStrVal = NULL;\r
+\r
+    pcTmp = vtoy_json_skip(*ppcEnd);\r
+    if ((NULL == pcTmp) || (':' != *pcTmp))\r
+    {\r
+        *ppcEnd = pcTmp;\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+    if (JSON_SUCCESS != Ret)\r
+    {\r
+        vdebug("Failed to parse array child.\n");\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    pcTmp = vtoy_json_skip(*ppcEnd);\r
+    while ((NULL != pcTmp) && (',' == *pcTmp))\r
+    {\r
+        JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);\r
+        pstJsonChild->pstNext = pstJsonItem;\r
+        pstJsonItem->pstPrev = pstJsonChild;\r
+        pstJsonChild = pstJsonItem;\r
+\r
+        Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+        if (JSON_SUCCESS != Ret)\r
+        {\r
+            vdebug("Failed to parse array child.\n");\r
+            return JSON_FAILED;\r
+        }\r
+\r
+        pcTmp = vtoy_json_skip(*ppcEnd);\r
+        pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;\r
+        pstJsonChild->unData.pcStrVal = NULL;\r
+        if ((NULL == pcTmp) || (':' != *pcTmp))\r
+        {\r
+            *ppcEnd = pcTmp;\r
+            return JSON_FAILED;\r
+        }\r
+\r
+        Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);\r
+        if (JSON_SUCCESS != Ret)\r
+        {\r
+            vdebug("Failed to parse array child.\n");\r
+            return JSON_FAILED;\r
+        }\r
+\r
+        pcTmp = vtoy_json_skip(*ppcEnd);\r
+    }\r
+\r
+    if ((NULL != pcTmp) && ('}' == *pcTmp))\r
+    {\r
+        *ppcEnd = pcTmp + 1;\r
+        return JSON_SUCCESS;\r
+    }\r
+    else\r
+    {\r
+        *ppcEnd = pcTmp;\r
+        return JSON_FAILED;\r
+    }\r
+}\r
+\r
+int vtoy_json_parse_value\r
+(\r
+    char *pcNewStart,\r
+    char *pcRawStart,\r
+    VTOY_JSON *pstJson, \r
+    const char *pcData,\r
+    const char **ppcEnd\r
+)\r
+{\r
+    pcData = vtoy_json_skip(pcData);\r
+    \r
+    switch (*pcData)\r
+    {\r
+        case 'n':\r
+        {\r
+            if (0 == strncmp(pcData, "null", 4))\r
+            {\r
+                pstJson->enDataType = JSON_TYPE_NULL;\r
+                *ppcEnd = pcData + 4;\r
+                return JSON_SUCCESS;\r
+            }\r
+            break;\r
+        }\r
+        case 'f':\r
+        {\r
+            if (0 == strncmp(pcData, "false", 5))\r
+            {\r
+                pstJson->enDataType = JSON_TYPE_BOOL;\r
+                pstJson->unData.lValue = 0;\r
+                *ppcEnd = pcData + 5;\r
+                return JSON_SUCCESS;\r
+            }\r
+            break;\r
+        }\r
+        case 't':\r
+        {\r
+            if (0 == strncmp(pcData, "true", 4))\r
+            {\r
+                pstJson->enDataType = JSON_TYPE_BOOL;\r
+                pstJson->unData.lValue = 1;\r
+                *ppcEnd = pcData + 4;\r
+                return JSON_SUCCESS;\r
+            }\r
+            break;\r
+        }\r
+        case '\"':\r
+        {\r
+            return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+        }\r
+        case '[':\r
+        {\r
+            return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+        }\r
+        case '{':\r
+        {\r
+            return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);\r
+        }\r
+        case '-':\r
+        {\r
+            return vtoy_json_parse_number(pstJson, pcData, ppcEnd);\r
+        }\r
+        default :\r
+        {\r
+            if (*pcData >= '0' && *pcData <= '9')\r
+            {\r
+                return vtoy_json_parse_number(pstJson, pcData, ppcEnd);\r
+            }\r
+        }\r
+    }\r
+\r
+    *ppcEnd = pcData;\r
+    vdebug("Invalid json data %u.\n", (uint8_t)(*pcData));\r
+    return JSON_FAILED;\r
+}\r
+\r
+VTOY_JSON * vtoy_json_create(void)\r
+{\r
+    VTOY_JSON *pstJson = NULL;\r
+\r
+    pstJson = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON));\r
+    if (NULL == pstJson)\r
+    {\r
+        return NULL;\r
+    }\r
+    \r
+    return pstJson;\r
+}\r
+\r
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)\r
+{\r
+    uint32_t uiMemSize = 0;\r
+    int Ret = JSON_SUCCESS;\r
+    char *pcNewBuf = NULL;\r
+    const char *pcEnd = NULL;\r
+\r
+    uiMemSize = strlen(szJsonData) + 1;\r
+    pcNewBuf = (char *)malloc(uiMemSize);\r
+    if (NULL == pcNewBuf)\r
+    {\r
+        vdebug("Failed to alloc new buf.\n");\r
+        return JSON_FAILED;\r
+    }\r
+    memcpy(pcNewBuf, szJsonData, uiMemSize);\r
+    pcNewBuf[uiMemSize - 1] = 0;\r
+\r
+    Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);\r
+    if (JSON_SUCCESS != Ret)\r
+    {\r
+        vdebug("Failed to parse json data %s start=%p, end=%p:%s.\n", \r
+                    szJsonData, szJsonData, pcEnd, pcEnd);\r
+        return JSON_FAILED;\r
+    }\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_parse\r
+(\r
+    const VTOY_JSON    *pstJson,\r
+    uint32_t            uiParseNum,\r
+    VTOY_JSON_PARSE_S  *pstJsonParse\r
+)\r
+{   \r
+    uint32_t i = 0;\r
+    const VTOY_JSON *pstJsonCur = NULL;\r
+    VTOY_JSON_PARSE_S *pstCurParse = NULL;\r
+\r
+    for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)\r
+    {\r
+        if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||\r
+            (JSON_TYPE_ARRAY == pstJsonCur->enDataType))\r
+        {\r
+            continue;\r
+        }\r
+\r
+        for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)\r
+        {\r
+            if (0 == strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))\r
+            {   \r
+                pstCurParse = pstJsonParse + i;\r
+                break;\r
+            }\r
+        }\r
+\r
+        if (NULL == pstCurParse)\r
+        {\r
+            continue;\r
+        }\r
+    \r
+        switch (pstJsonCur->enDataType)\r
+        {\r
+            case JSON_TYPE_NUMBER:\r
+            {\r
+                if (sizeof(uint32_t) == pstCurParse->uiBufSize)\r
+                {\r
+                    *(uint32_t *)(pstCurParse->pDataBuf) = (uint32_t)pstJsonCur->unData.lValue;\r
+                }\r
+                else if (sizeof(uint16_t) == pstCurParse->uiBufSize)\r
+                {\r
+                    *(uint16_t *)(pstCurParse->pDataBuf) = (uint16_t)pstJsonCur->unData.lValue;\r
+                }\r
+                else if (sizeof(uint8_t) == pstCurParse->uiBufSize)\r
+                {\r
+                    *(uint8_t *)(pstCurParse->pDataBuf) = (uint8_t)pstJsonCur->unData.lValue;\r
+                }\r
+                else if ((pstCurParse->uiBufSize > sizeof(uint64_t)))\r
+                {\r
+                    snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu", \r
+                        (unsigned long long)(pstJsonCur->unData.lValue));\r
+                }\r
+                else\r
+                {\r
+                    vdebug("Invalid number data buf size %u.\n", pstCurParse->uiBufSize);\r
+                }\r
+                break;\r
+            }\r
+            case JSON_TYPE_STRING:\r
+            {\r
+                strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);\r
+                break;\r
+            }\r
+            case JSON_TYPE_BOOL:\r
+            {\r
+                *(uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;\r
+                break;\r
+            }\r
+            default :\r
+            {\r
+                break;\r
+            }\r
+        }\r
+    }\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_array\r
+(\r
+     VTOY_JSON *pstJson, \r
+     const char *szKey, \r
+     VTOY_JSON **ppstArrayItem\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *ppstArrayItem = pstJsonItem;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_array_ex\r
+(\r
+     VTOY_JSON *pstJson, \r
+     const char *szKey, \r
+     VTOY_JSON **ppstArrayItem\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+    \r
+    *ppstArrayItem = pstJsonItem->pstChild;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_scan_object\r
+(\r
+     VTOY_JSON *pstJson, \r
+     const char *szKey, \r
+     VTOY_JSON **ppstObjectItem\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+\r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *ppstObjectItem = pstJsonItem;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_int\r
+(\r
+    VTOY_JSON *pstJson, \r
+    const char *szKey, \r
+    int *piValue\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *piValue = (int)pstJsonItem->unData.lValue;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_uint\r
+(\r
+    VTOY_JSON *pstJson, \r
+    const char *szKey, \r
+    uint32_t *puiValue\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *puiValue = (uint32_t)pstJsonItem->unData.lValue;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_uint64\r
+(\r
+    VTOY_JSON *pstJson, \r
+    const char *szKey, \r
+    uint64_t *pui64Value\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *pui64Value = (uint64_t)pstJsonItem->unData.lValue;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_bool\r
+(\r
+    VTOY_JSON *pstJson,\r
+    const char *szKey, \r
+    uint8_t *pbValue\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    *pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+int vtoy_json_get_string\r
+(\r
+     VTOY_JSON *pstJson, \r
+     const char *szKey, \r
+     uint32_t  uiBufLen,\r
+     char *pcBuf\r
+)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+    \r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return JSON_NOT_FOUND;\r
+    }\r
+\r
+    strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);\r
+\r
+    return JSON_SUCCESS;\r
+}\r
+\r
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson,  const char *szKey)\r
+{\r
+    VTOY_JSON *pstJsonItem = NULL;\r
+\r
+    if ((NULL == pstJson) || (NULL == szKey))\r
+    {\r
+        return NULL;\r
+    }\r
+\r
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);\r
+    if (NULL == pstJsonItem)\r
+    {\r
+        vdebug("Key %s is not found in json data.\n", szKey);\r
+        return NULL;\r
+    }\r
+\r
+    return pstJsonItem->unData.pcStrVal;\r
+}\r
+\r
+int vtoy_json_destroy(VTOY_JSON *pstJson)\r
+{\r
+    if (NULL == pstJson)\r
+    {   \r
+        return JSON_SUCCESS;\r
+    }\r
+\r
+    if (NULL != pstJson->pstChild)\r
+    {\r
+        vtoy_json_free(pstJson->pstChild);\r
+    }\r
+\r
+    if (NULL != pstJson->pstNext)\r
+    {\r
+        vtoy_json_free(pstJson->pstNext);\r
+    }\r
+\r
+    free(pstJson);\r
+    \r
+    return JSON_SUCCESS;\r
+}\r
+\r
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_json.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.h
new file mode 100644 (file)
index 0000000..12e6f19
--- /dev/null
@@ -0,0 +1,267 @@
+/******************************************************************************
+ * ventoy_json.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_JSON_H__
+#define __VENTOY_JSON_H__
+
+#define JSON_NEW_ITEM(pstJson, ret) \
+{ \
+    (pstJson) = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON)); \
+    if (NULL == (pstJson)) \
+    { \
+        vdebug("Failed to alloc memory for json."); \
+        return (ret); \
+    } \
+}
+
+#define ssprintf(curpos, buf, len, fmt, args...) \
+    curpos += snprintf(buf + curpos, len - curpos, fmt, ##args)
+
+#define VTOY_JSON_IS_SKIPABLE(c) (((c) <= 32) ? 1 : 0)
+
+#define VTOY_JSON_PRINT_PREFIX(uiDepth, args...) \
+{ \
+    uint32_t _uiLoop = 0; \
+    for (_uiLoop = 0; _uiLoop < (uiDepth); _uiLoop++) \
+    { \
+        ssprintf(uiCurPos, pcBuf, uiBufLen, "    "); \
+    } \
+    ssprintf(uiCurPos, pcBuf, uiBufLen, ##args); \
+}
+
+#define VTOY_JSON_SUCCESS_RET      "{ \"result\" : \"success\" }"
+#define VTOY_JSON_FAILED_RET       "{ \"result\" : \"failed\" }"
+#define VTOY_JSON_INVALID_RET      "{ \"result\" : \"invalidfmt\" }"
+#define VTOY_JSON_TOKEN_ERR_RET    "{ \"result\" : \"tokenerror\" }"
+#define VTOY_JSON_EXIST_RET        "{ \"result\" : \"exist\" }"
+#define VTOY_JSON_TIMEOUT_RET      "{ \"result\" : \"timeout\" }"
+#define VTOY_JSON_BUSY_RET         "{ \"result\" : \"busy\" }"
+#define VTOY_JSON_INUSE_RET        "{ \"result\" : \"inuse\" }"
+#define VTOY_JSON_NOTFOUND_RET     "{ \"result\" : \"notfound\" }"
+#define VTOY_JSON_NOTRUNNING_RET   "{ \"result\" : \"notrunning\" }"
+#define VTOY_JSON_NOT_READY_RET    "{ \"result\" : \"notready\" }"
+#define VTOY_JSON_NOT_SUPPORT_RET  "{ \"result\" : \"notsupport\" }"
+#define VTOY_JSON_MBR_2TB_RET      "{ \"result\" : \"mbr2tb\" }"
+#define VTOY_JSON_INVALID_RSV_RET      "{ \"result\" : \"reserve_invalid\" }"
+#define VTOY_JSON_FILE_NOT_FOUND_RET     "{ \"result\" : \"file_not_found\" }"
+
+typedef enum tagJSON_TYPE
+{
+    JSON_TYPE_NUMBER = 0,
+    JSON_TYPE_STRING,
+    JSON_TYPE_BOOL,
+    JSON_TYPE_ARRAY,
+    JSON_TYPE_OBJECT,
+    JSON_TYPE_NULL,
+    JSON_TYPE_BUTT
+}JSON_TYPE;
+
+typedef struct tagVTOY_JSON
+{
+    struct tagVTOY_JSON *pstPrev;
+    struct tagVTOY_JSON *pstNext;
+    struct tagVTOY_JSON *pstChild;
+
+    JSON_TYPE enDataType;
+    union 
+    {
+        char  *pcStrVal;
+        int    iNumVal;
+        uint64_t lValue;
+    }unData;
+
+    char *pcName;
+}VTOY_JSON;
+
+#define VTOY_JSON_FMT_BEGIN(uiCurPos, pcBuf, uiBufLen) \
+{\
+    uint32_t __uiCurPos = (uiCurPos);\
+    uint32_t __uiBufLen = (uiBufLen);\
+    char *__pcBuf = (pcBuf);
+    
+#define VTOY_JSON_FMT_END(uiCurPos) \
+    (uiCurPos) = __uiCurPos;\
+}
+    
+#define VTOY_JSON_FMT_OBJ_BEGIN()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "{")
+    
+#define VTOY_JSON_FMT_OBJ_END()  \
+{\
+    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+    {\
+        __uiCurPos -= 1;\
+    }\
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
+}
+
+#define VTOY_JSON_FMT_OBJ_ENDEX()  \
+{\
+    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+    {\
+        __uiCurPos -= 1;\
+    }\
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "},");\
+}
+    
+#define VTOY_JSON_FMT_KEY(Key)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":", (Key))
+
+#define VTOY_JSON_FMT_ITEM(Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\",", (Item))
+
+#define VTOY_JSON_FMT_COMA()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");
+
+#define VTOY_JSON_FMT_APPEND_BEGIN() \
+{ \
+    if ('}' == *(__pcBuf + (__uiCurPos - 1)))\
+    {\
+        __uiCurPos -= 1;\
+    }\
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");\
+}
+
+#define VTOY_JSON_FMT_APPEND_END() \
+{ \
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
+}
+
+#define VTOY_JSON_FMT_ARY_BEGIN()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "[")
+    
+#define VTOY_JSON_FMT_ARY_END()    \
+{\
+    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+    {\
+        __uiCurPos -= 1;\
+    }\
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "]");\
+}
+
+#define VTOY_JSON_FMT_ARY_ENDEX()    \
+{\
+    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
+    {\
+        __uiCurPos -= 1;\
+    }\
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "],");\
+}
+    
+#define VTOY_JSON_FMT_UINT64(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%llu,", Key, (_ull)Val)
+
+#define VTOY_JSON_FMT_ULONG(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%lu,", Key, Val)
+#define VTOY_JSON_FMT_LONG(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%ld,", Key, Val)
+
+#define VTOY_JSON_FMT_UINT(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%u,", Key, Val)
+
+#define VTOY_JSON_FMT_STRINT(Key, Val)  \
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%u\",", Key, Val)
+
+#define VTOY_JSON_FMT_STRINT64(Key, Val)  \
+    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%llu\",", Key, Val)
+
+#define VTOY_JSON_FMT_SINT(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%d,", Key, Val)
+    
+#define VTOY_JSON_FMT_DUBL(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%.1lf,", Key, Val)
+#define VTOY_JSON_FMT_DUBL2(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%10.02lf,", Key, Val)
+
+#define VTOY_JSON_FMT_STRN(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%s\",", Key, Val) 
+    
+#define VTOY_JSON_FMT_NULL(Key)       ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":null,", Key)
+
+#define VTOY_JSON_FMT_TRUE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key))
+#define VTOY_JSON_FMT_FALSE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key))
+
+#define VTOY_JSON_FMT_BOOL(Key, Val)  \
+{\
+    if (0 == (Val))\
+    {\
+        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key));\
+    }\
+    else \
+    {\
+        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key));\
+    }\
+}
+
+typedef struct tagVTOY_JSON_PARSE
+{
+    char *pcKey;
+    void *pDataBuf;
+    uint32_t  uiBufSize;
+}VTOY_JSON_PARSE_S;
+
+#define JSON_SUCCESS    0
+#define JSON_FAILED     1
+#define JSON_NOT_FOUND  2
+
+int vtoy_json_parse_value
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+);
+VTOY_JSON * vtoy_json_create(void);
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData);
+int vtoy_json_destroy(VTOY_JSON *pstJson);
+VTOY_JSON *vtoy_json_find_item
+(
+    VTOY_JSON *pstJson,
+    JSON_TYPE  enDataType,
+    const char *szKey
+);
+int vtoy_json_scan_parse
+(
+    const VTOY_JSON    *pstJson,
+    uint32_t            uiParseNum,
+    VTOY_JSON_PARSE_S  *pstJsonParse
+);
+int vtoy_json_get_int
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    int *piValue
+);
+int vtoy_json_get_uint
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    uint32_t *puiValue
+);
+int vtoy_json_get_uint64
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    uint64_t *pui64Value
+);
+int vtoy_json_get_bool
+(
+    VTOY_JSON *pstJson,
+    const char *szKey, 
+    uint8_t *pbValue
+);
+int vtoy_json_get_string
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     uint32_t  uiBufLen,
+     char *pcBuf
+);
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson,  const char *szKey);
+
+#endif /* __VENTOY_JSON_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_log.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_log.c
new file mode 100644 (file)
index 0000000..469f843
--- /dev/null
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * ventoy_log.c  ---- ventoy log
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <ventoy_define.h>
+
+static int g_ventoy_log_level = VLOG_DEBUG;
+static pthread_mutex_t g_log_mutex;
+
+int ventoy_log_init(void)
+{
+    pthread_mutex_init(&g_log_mutex, NULL);
+    return 0;
+}
+
+void ventoy_log_exit(void)
+{
+    pthread_mutex_destroy(&g_log_mutex);
+}
+
+void ventoy_set_loglevel(int level)
+{
+    g_ventoy_log_level = level;
+}
+
+void ventoy_syslog_newline(int level, const char *Fmt, ...)
+{
+    char log[512];
+    va_list arg;
+    time_t stamp;
+    struct tm ttm;
+    FILE *fp;
+    
+    if (level > g_ventoy_log_level)
+    {
+        return;
+    }
+
+    time(&stamp);
+    localtime_r(&stamp, &ttm);
+
+    va_start(arg, Fmt);
+    vsnprintf(log, 512, Fmt, arg);
+    va_end(arg);
+
+    pthread_mutex_lock(&g_log_mutex);
+    fp = fopen(VTOY_LOG_FILE, "a+");
+    if (fp)
+    {
+        fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s\n", 
+           ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
+           ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
+           log);
+        fclose(fp);
+    }
+    pthread_mutex_unlock(&g_log_mutex);
+}
+
+void ventoy_syslog(int level, const char *Fmt, ...)
+{
+    char log[512];
+    va_list arg;
+    time_t stamp;
+    struct tm ttm;
+    FILE *fp;
+    
+    if (level > g_ventoy_log_level)
+    {
+        return;
+    }
+
+    time(&stamp);
+    localtime_r(&stamp, &ttm);
+
+    va_start(arg, Fmt);
+    vsnprintf(log, 512, Fmt, arg);
+    va_end(arg);
+
+    pthread_mutex_lock(&g_log_mutex);
+    fp = fopen(VTOY_LOG_FILE, "a+");
+    if (fp)
+    {
+        fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s", 
+           ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
+           ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
+           log);
+        fclose(fp);
+    }
+    pthread_mutex_unlock(&g_log_mutex);
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c
new file mode 100644 (file)
index 0000000..ba79d54
--- /dev/null
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * ventoy_md5.c  ---- ventoy md5
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+const static uint32_t k[64] = 
+{
+    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 
+};
+
+const static uint32_t r[] = 
+{
+    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
+    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
+};
+
+#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
+#define to_bytes(val, bytes) *((uint32_t *)(bytes)) = (val)
+
+#define ROTATE_CALC() \
+{\
+    temp = d; \
+    d = c; \
+    c = b; \
+    b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); \
+    a = temp; \
+}
+
+void ventoy_md5(const void *data, uint32_t len, uint8_t *md5)
+{ 
+    uint32_t h0, h1, h2, h3;
+    uint32_t w[16];
+    uint32_t a, b, c, d, i, f, g, temp;
+    uint32_t offset, mod, delta;
+    uint8_t postbuf[128] = {0};
+    
+    // Initialize variables - simple count in nibbles:
+    h0 = 0x67452301;
+    h1 = 0xefcdab89;
+    h2 = 0x98badcfe;
+    h3 = 0x10325476;
+    //Pre-processing:
+    //append "1" bit to message    
+    //append "0" bits until message length in bits ≡ 448 (mod 512)
+    //append length mod (2^64) to message
+
+    mod = len % 64;
+    if (mod)
+    {
+        memcpy(postbuf, (const uint8_t *)data + len - mod, mod);            
+    }
+    
+    postbuf[mod] = 0x80;
+    if (mod < 56)
+    {
+        to_bytes(len * 8, postbuf + 56);
+        to_bytes(len >> 29, postbuf + 60);
+        delta = 64;
+    }
+    else
+    {
+        to_bytes(len * 8, postbuf + 120);
+        to_bytes(len >> 29, postbuf + 124);
+        delta = 128;
+    }
+
+    len -= mod;
+    for (offset = 0; offset < len + delta; offset += 64)
+    {
+        if (offset < len)
+        {
+            memcpy(w, (const uint8_t *)data + offset, 64);
+        }
+        else
+        {
+            memcpy(w, postbuf + offset - len, 64);
+        }
+
+        // Initialize hash value for this chunk:
+        a = h0;
+        b = h1;
+        c = h2;
+        d = h3;
+        // Main loop:
+        for (i = 0; i < 16; i++)
+        {
+            f = (b & c) | ((~b) & d);
+            g = i;            
+            ROTATE_CALC();
+        }
+        
+        for (i = 16; i < 32; i++)
+        {
+            f = (d & b) | ((~d) & c);
+            g = (5 * i + 1) % 16;
+            ROTATE_CALC();
+        }
+        
+        for (i = 32; i < 48; i++)
+        {
+            f = b ^ c ^ d;
+            g = (3 * i + 5) % 16;
+            ROTATE_CALC();
+        }
+        
+        for (i = 48; i < 64; i++)
+        {
+            f = c ^ (b | (~d));
+            g = (7 * i) % 16;
+            ROTATE_CALC();
+        }
+        
+        // Add this chunk's hash to result so far:
+        h0 += a;
+        h1 += b;
+        h2 += c;
+        h3 += d;
+    }
+    //var char md5[16] := h0 append h1 append h2 append h3 //(Output is in little-endian)
+    to_bytes(h0, md5);
+    to_bytes(h1, md5 + 4);
+    to_bytes(h2, md5 + 8);
+    to_bytes(h3, md5 + 12);
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_util.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.c
new file mode 100644 (file)
index 0000000..562c3a0
--- /dev/null
@@ -0,0 +1,541 @@
+/******************************************************************************
+ * ventoy_util.c  ---- ventoy util
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <time.h>
+#include <ventoy_define.h>
+#include <ventoy_util.h>
+
+uint8_t g_mbr_template[512];
+
+void ventoy_gen_preudo_uuid(void *uuid)
+{
+    int i;
+    int fd;
+
+    fd = open("/dev/urandom", O_RDONLY | O_BINARY);
+    if (fd < 0)
+    {
+        srand(time(NULL));
+        for (i = 0; i < 8; i++)
+        {
+            *((uint16_t *)uuid + i) = (uint16_t)(rand() & 0xFFFF);
+        }
+    }
+    else
+    {
+        read(fd, uuid, 16);
+        close(fd);
+    }
+}
+
+uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes)
+{
+    int i;
+    int Pow2 = 1;
+    double Delta;
+    double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
+
+    if ((SizeBytes % SIZE_1GB) == 0)
+    {
+        return (uint64_t)(SizeBytes / SIZE_1GB);
+    }
+
+    for (i = 0; i < 12; i++)
+    {
+        if (Pow2 > GB)
+        {
+            Delta = (Pow2 - GB) / Pow2;
+        }
+        else
+        {
+            Delta = (GB - Pow2) / Pow2;
+        }
+
+        if (Delta < 0.05)
+        {
+            return Pow2;
+        }
+
+        Pow2 <<= 1;
+    }
+
+    return (uint64_t)GB;
+}
+
+
+int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...)
+{
+    int len;
+    char c;
+    char path[256];
+    va_list arg;
+
+    va_start(arg, fmt);
+    vsnprintf(path, 256, fmt, arg);
+    va_end(arg);
+
+    if (access(path, F_OK) >= 0)
+    {
+        FILE *fp = fopen(path, "r");
+        memset(buffer, 0, buflen);
+        len = (int)fread(buffer, 1, buflen - 1, fp);
+        fclose(fp);
+
+        while (len > 0)
+        {
+            c = buffer[len - 1];
+            if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
+            {
+                buffer[len - 1] = 0;
+                len--;
+            }
+            else
+            {
+                break;
+            }
+        }
+        
+        return 0;
+    }
+    else
+    {
+        vdebug("%s not exist \n", path);
+        return 1;
+    }
+}
+
+int ventoy_is_disk_mounted(const char *devpath)
+{
+    int len;
+    int mount = 0;
+    char line[512];
+    FILE *fp = NULL;
+
+    fp = fopen("/proc/mounts", "r");
+    if (!fp)
+    {
+        return 0;
+    }
+
+    len = (int)strlen(devpath);
+    while (fgets(line, sizeof(line), fp))
+    {
+        if (strncmp(line, devpath, len) == 0)
+        {
+            mount = 1;
+            vdebug("%s mounted <%s>\n", devpath, line);
+            goto end;
+        }
+    }
+
+end:
+    fclose(fp);
+    return mount;
+}
+
+int ventoy_try_umount_disk(const char *devpath)
+{
+    int rc;
+    int len;
+    char line[512];
+    char *pos1 = NULL;
+    char *pos2 = NULL;
+    FILE *fp = NULL;
+
+    fp = fopen("/proc/mounts", "r");
+    if (!fp)
+    {
+        return 0;
+    }
+
+    len = (int)strlen(devpath);
+    while (fgets(line, sizeof(line), fp))
+    {
+        if (strncmp(line, devpath, len) == 0)
+        {
+            pos1 = strchr(line, ' ');
+            if (pos1)
+            {
+                pos2 = strchr(pos1 + 1, ' ');
+                if (pos2)
+                {
+                    *pos2 = 0;
+                }
+
+                rc = umount(pos1 + 1);
+                if (rc)
+                {
+                    vdebug("umount %s %s [ failed ] error:%d\n", devpath, pos1 + 1, errno);                                        
+                }
+                else
+                {
+                    vdebug("umount %s %s [ success ]\n", devpath, pos1 + 1);
+                }
+            }
+        }
+    }
+
+    fclose(fp);
+    return 0;
+}
+
+
+int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen)
+{
+    int FileSize;
+    FILE *fp = NULL;
+    void *Data = NULL;
+
+    fp = fopen(FileName, "rb");
+    if (fp == NULL)
+    {
+        vlog("Failed to open file %s", FileName);
+        return 1;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    FileSize = (int)ftell(fp);
+
+    Data = malloc(FileSize + ExtLen);
+    if (!Data)
+    {
+        fclose(fp);
+        return 1;
+    }
+
+    fseek(fp, 0, SEEK_SET);
+    fread(Data, 1, FileSize, fp);
+
+    fclose(fp);
+
+    *Bufer = Data;
+    *BufLen = FileSize;
+
+    return 0;
+}
+
+const char * ventoy_get_local_version(void)
+{
+    int rc;
+    int FileSize;
+    char *Pos = NULL;
+    char *Buf = NULL;
+    static char LocalVersion[64] = { 0 };
+
+    if (LocalVersion[0] == 0)
+    {
+        rc = ventoy_read_file_to_buf("ventoy/version", 1, (void **)&Buf, &FileSize);
+        if (rc)
+        {
+            return "";
+        }
+        Buf[FileSize] = 0;
+
+        for (Pos = Buf; *Pos; Pos++)
+        {
+            if (*Pos == '\r' || *Pos == '\n')
+            {
+                *Pos = 0;
+                break;
+            }
+        }
+
+        scnprintf(LocalVersion, "%s", Buf);
+        free(Buf);
+    }
+    
+    return LocalVersion;
+}
+
+int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
+{
+    memcpy(pMBR, g_mbr_template, 512);
+    return 0;    
+}
+
+static int VentoyFillProtectMBR(uint64_t DiskSizeBytes, MBR_HEAD *pMBR)
+{
+    ventoy_guid Guid;
+    uint32_t DiskSignature;
+    uint64_t DiskSectorCount;
+
+    VentoyGetLocalBootImg(pMBR);
+
+    ventoy_gen_preudo_uuid(&Guid);
+
+    memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
+
+    vdebug("Disk signature: 0x%08x\n", DiskSignature);
+
+    memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
+
+    DiskSectorCount = DiskSizeBytes / 512 - 1;
+    if (DiskSectorCount > 0xFFFFFFFF)
+    {
+        DiskSectorCount = 0xFFFFFFFF;
+    }
+
+    memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));
+
+    pMBR->PartTbl[0].Active = 0x00;
+    pMBR->PartTbl[0].FsFlag = 0xee; // EE
+
+    pMBR->PartTbl[0].StartHead = 0;
+    pMBR->PartTbl[0].StartSector = 1;
+    pMBR->PartTbl[0].StartCylinder = 0;
+    pMBR->PartTbl[0].EndHead = 254;
+    pMBR->PartTbl[0].EndSector = 63;
+    pMBR->PartTbl[0].EndCylinder = 1023;
+
+    pMBR->PartTbl[0].StartSectorId = 1;
+    pMBR->PartTbl[0].SectorCount = (uint32_t)DiskSectorCount;
+
+    pMBR->Byte55 = 0x55;
+    pMBR->ByteAA = 0xAA;
+
+    pMBR->BootCode[92] = 0x22;
+
+    return 0;
+}
+
+static int ventoy_fill_gpt_partname(uint16_t Name[36], const char *asciiName)
+{
+    int i;
+    int len;
+
+    memset(Name, 0, 36 * sizeof(uint16_t));
+    len = (int)strlen(asciiName);
+    for (i = 0; i < 36 && i < len; i++)
+    {
+        Name[i] = asciiName[i];
+    }
+    
+    return 0;
+}
+
+int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt)
+{
+    uint64_t ReservedSector = 33;
+    uint64_t Part1SectorCount = 0;
+    uint64_t DiskSectorCount = size / 512;
+    VTOY_GPT_HDR *Head = &gpt->Head;
+    VTOY_GPT_PART_TBL *Table = gpt->PartTbl;
+    ventoy_guid WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
+    //ventoy_guid EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
+       //ventoy_guid BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
+
+    VentoyFillProtectMBR(size, &gpt->MBR);
+
+    if (reserve > 0)
+    {
+        ReservedSector += reserve / 512;
+    }
+
+    // check aligned with 4KB
+    if (align4k)
+    {
+        if (DiskSectorCount % 8)
+        {
+            vdebug("Disk need to align with 4KB %u\n", (uint32_t)(DiskSectorCount % 8));
+            ReservedSector += (DiskSectorCount % 8);
+        }
+    }
+
+    Part1SectorCount = DiskSectorCount - ReservedSector - (VTOYEFI_PART_BYTES / 512) - 2048;
+
+    memcpy(Head->Signature, "EFI PART", 8);
+    Head->Version[2] = 0x01;
+    Head->Length = 92;
+    Head->Crc = 0;
+    Head->EfiStartLBA = 1;
+    Head->EfiBackupLBA = DiskSectorCount - 1;
+    Head->PartAreaStartLBA = 34;
+    Head->PartAreaEndLBA = DiskSectorCount - 34;
+    ventoy_gen_preudo_uuid(&Head->DiskGuid);
+    Head->PartTblStartLBA = 2;
+    Head->PartTblTotNum = 128;
+    Head->PartTblEntryLen = 128;
+
+
+    memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
+    ventoy_gen_preudo_uuid(&(Table[0].PartGuid));
+    Table[0].StartLBA = 2048;
+    Table[0].LastLBA = 2048 + Part1SectorCount - 1;
+    Table[0].Attr = 0;
+    ventoy_fill_gpt_partname(Table[0].Name, "Ventoy");
+
+    // to fix windows issue
+    //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
+    memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
+    ventoy_gen_preudo_uuid(&(Table[1].PartGuid));
+    Table[1].StartLBA = Table[0].LastLBA + 1;
+    Table[1].LastLBA = Table[1].StartLBA + VTOYEFI_PART_BYTES / 512 - 1;
+    Table[1].Attr = 0x8000000000000001ULL;
+    ventoy_fill_gpt_partname(Table[1].Name, "VTOYEFI");
+
+#if 0
+       memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(ventoy_guid));
+       ventoy_gen_preudo_uuid(&(Table[2].PartGuid));
+       Table[2].StartLBA = 34;
+       Table[2].LastLBA = 2047;
+       Table[2].Attr = 0;
+#endif
+
+    //Update CRC
+    Head->PartTblCrc = ventoy_crc32(Table, sizeof(gpt->PartTbl));
+    Head->Crc = ventoy_crc32(Head, Head->Length);
+
+    return 0;
+}
+
+int VentoyFillMBRLocation(uint64_t DiskSizeInBytes, uint32_t StartSectorId, uint32_t SectorCount, PART_TABLE *Table)
+{
+    uint8_t Head;
+    uint8_t Sector;
+    uint8_t nSector = 63;
+    uint8_t nHead = 8;    
+    uint32_t Cylinder;
+    uint32_t EndSectorId;
+
+    while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
+    {
+        nHead = (uint8_t)nHead * 2;
+    }
+
+    if (nHead == 0)
+    {
+        nHead = 255;
+    }
+
+    Cylinder = StartSectorId / nSector / nHead;
+    Head = StartSectorId / nSector % nHead;
+    Sector = StartSectorId % nSector + 1;
+
+    Table->StartHead = Head;
+    Table->StartSector = Sector;
+    Table->StartCylinder = Cylinder;
+
+    EndSectorId = StartSectorId + SectorCount - 1;
+    Cylinder = EndSectorId / nSector / nHead;
+    Head = EndSectorId / nSector % nHead;
+    Sector = EndSectorId % nSector + 1;
+
+    Table->EndHead = Head;
+    Table->EndSector = Sector;
+    Table->EndCylinder = Cylinder;
+
+    Table->StartSectorId = StartSectorId;
+    Table->SectorCount = SectorCount;
+
+    return 0;
+}
+
+int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR)
+{
+    ventoy_guid Guid;
+    uint32_t DiskSignature;
+    uint32_t DiskSectorCount;
+    uint32_t PartSectorCount;
+    uint32_t PartStartSector;
+       uint32_t ReservedSector;
+
+    VentoyGetLocalBootImg(pMBR);
+
+    ventoy_gen_preudo_uuid(&Guid);
+
+    memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
+
+    vdebug("Disk signature: 0x%08x\n", DiskSignature);
+
+    memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
+
+    if (size / 512 > 0xFFFFFFFF)
+    {
+        DiskSectorCount = 0xFFFFFFFF;
+    }
+    else
+    {
+        DiskSectorCount = (uint32_t)(size / 512);
+    }
+
+       if (reserve <= 0)
+       {
+               ReservedSector = 0;
+       }
+       else
+       {
+               ReservedSector = (uint32_t)(reserve / 512);
+       }
+
+    if (PartStyle)
+    {
+        ReservedSector += 33; // backup GPT part table
+    }
+
+    // check aligned with 4KB
+    if (align4k)
+    {
+        uint64_t sectors = size / 512;
+        if (sectors % 8)
+        {
+            vlog("Disk need to align with 4KB %u\n", (uint32_t)(sectors % 8));
+            ReservedSector += (uint32_t)(sectors % 8);
+        }
+    }
+
+       vlog("ReservedSector: %u\n", ReservedSector);
+
+    
+    //Part1
+    PartStartSector = VTOYIMG_PART_START_SECTOR;
+       PartSectorCount = DiskSectorCount - ReservedSector - VTOYEFI_PART_BYTES / 512 - PartStartSector;
+    VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl);
+
+    pMBR->PartTbl[0].Active = 0x80; // bootable
+    pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
+
+    //Part2
+    PartStartSector += PartSectorCount;
+    PartSectorCount = VTOYEFI_PART_BYTES / 512;
+    VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
+
+    pMBR->PartTbl[1].Active = 0x00; 
+    pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
+
+    pMBR->Byte55 = 0x55;
+    pMBR->ByteAA = 0xAA;
+
+    return 0;
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_util.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.h
new file mode 100644 (file)
index 0000000..eb09310
--- /dev/null
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * ventoy_util.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_UTIL_H__
+#define __VENTOY_UTIL_H__
+
+#define check_free(p) if (p) free(p)
+#define vtoy_safe_close_fd(fd) \
+{\
+    if ((fd) >= 0) \
+    { \
+        close(fd);  \
+        (fd) = -1; \
+    }\
+}
+
+extern uint8_t g_mbr_template[512];
+void ventoy_gen_preudo_uuid(void *uuid);
+int ventoy_get_disk_part_name(const char *dev, int part, char *partbuf, int bufsize);
+int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...);
+uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes);
+void ventoy_md5(const void *data, uint32_t len, uint8_t *md5);
+int ventoy_is_disk_mounted(const char *devpath);
+int ventoy_try_umount_disk(const char *devpath);
+int unxz(unsigned char *in, int in_size,
+        int (*fill)(void *dest, unsigned int size),
+        int (*flush)(void *src, unsigned int size),
+        unsigned char *out, int *in_used,
+        void (*error)(char *x));
+int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen);
+const char * ventoy_get_local_version(void);
+int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt);
+int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR);
+
+#endif /* __VENTOY_UTIL_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h b/LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h
new file mode 100644 (file)
index 0000000..32f452c
--- /dev/null
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Ventoy2Disk.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY2DISK_H__
+#define __VENTOY2DISK_H__
+
+
+
+#endif /* __VENTOY2DISK_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh b/LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh
new file mode 100644 (file)
index 0000000..00a83c6
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+CUR="$PWD"
+
+rm -rf src
+mkdir -p src/libexfat
+mkdir -p src/mkfs
+
+rm -rf exfat-1.3.0
+unzip exfat-1.3.0.zip
+
+cd exfat-1.3.0
+autoreconf --install
+./configure --prefix="$CUR" CFLAGS='-O2 -D_FILE_OFFSET_BITS=64'
+make
+
+cp -a libexfat/*.c ../src/libexfat/
+cp -a libexfat/*.h ../src/libexfat/
+cp -a mkfs/*.c ../src/mkfs/
+cp -a mkfs/*.h ../src/mkfs/
+rm -f ../src/libexfat/log.c
+
+cd ..
+rm -rf exfat-1.3.0
+
+mv src/mkfs/main.c src/mkfs/mkexfat_main.c
+sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat_main.c
+sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat.h
+sed 's/int main/int mkexfat_main/g' -i src/mkfs/mkexfat_main.c
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip b/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip
new file mode 100644 (file)
index 0000000..aa1d5f3
Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip differ
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h
new file mode 100644 (file)
index 0000000..ba54410
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+       byteorder.h (12.01.10)
+       Endianness stuff. exFAT uses little-endian byte order.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#include "platform.h"
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct { uint16_t __u16; } le16_t;
+typedef struct { uint32_t __u32; } le32_t;
+typedef struct { uint64_t __u64; } le64_t;
+
+#if EXFAT_BYTE_ORDER == EXFAT_LITTLE_ENDIAN
+
+static inline uint16_t le16_to_cpu(le16_t v) { return v.__u16; }
+static inline uint32_t le32_to_cpu(le32_t v) { return v.__u32; }
+static inline uint64_t le64_to_cpu(le64_t v) { return v.__u64; }
+
+static inline le16_t cpu_to_le16(uint16_t v) { le16_t t = {v}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v) { le32_t t = {v}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v) { le64_t t = {v}; return t; }
+
+typedef size_t bitmap_t;
+
+#elif EXFAT_BYTE_ORDER == EXFAT_BIG_ENDIAN
+
+static inline uint16_t le16_to_cpu(le16_t v)
+       { return exfat_bswap16(v.__u16); }
+static inline uint32_t le32_to_cpu(le32_t v)
+       { return exfat_bswap32(v.__u32); }
+static inline uint64_t le64_to_cpu(le64_t v)
+       { return exfat_bswap64(v.__u64); }
+
+static inline le16_t cpu_to_le16(uint16_t v)
+       { le16_t t = {exfat_bswap16(v)}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v)
+       { le32_t t = {exfat_bswap32(v)}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v)
+       { le64_t t = {exfat_bswap64(v)}; return t; }
+
+typedef unsigned char bitmap_t;
+
+#else
+#error Wow! You have a PDP machine?!
+#endif
+
+#endif /* ifndef BYTEORDER_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c
new file mode 100644 (file)
index 0000000..15cd9aa
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+       cluster.c (03.09.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+/*
+ * Sector to absolute offset.
+ */
+static off_t s2o(const struct exfat* ef, off_t sector)
+{
+       return sector << ef->sb->sector_bits;
+}
+
+/*
+ * Cluster to sector.
+ */
+static off_t c2s(const struct exfat* ef, cluster_t cluster)
+{
+       if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+               exfat_bug("invalid cluster number %u", cluster);
+       return le32_to_cpu(ef->sb->cluster_sector_start) +
+               ((off_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) << ef->sb->spc_bits);
+}
+
+/*
+ * Cluster to absolute offset.
+ */
+off_t exfat_c2o(const struct exfat* ef, cluster_t cluster)
+{
+       return s2o(ef, c2s(ef, cluster));
+}
+
+/*
+ * Sector to cluster.
+ */
+static cluster_t s2c(const struct exfat* ef, off_t sector)
+{
+       return ((sector - le32_to_cpu(ef->sb->cluster_sector_start)) >>
+                       ef->sb->spc_bits) + EXFAT_FIRST_DATA_CLUSTER;
+}
+
+/*
+ * Size in bytes to size in clusters (rounded upwards).
+ */
+static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes)
+{
+       uint64_t cluster_size = CLUSTER_SIZE(*ef->sb);
+       return DIV_ROUND_UP(bytes, cluster_size);
+}
+
+cluster_t exfat_next_cluster(const struct exfat* ef,
+               const struct exfat_node* node, cluster_t cluster)
+{
+       le32_t next;
+       off_t fat_offset;
+
+       if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+               exfat_bug("bad cluster 0x%x", cluster);
+
+       if (node->is_contiguous)
+               return cluster + 1;
+       fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+               + cluster * sizeof(cluster_t);
+       if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0)
+               return EXFAT_CLUSTER_BAD; /* the caller should handle this and print
+                                            appropriate error message */
+       return le32_to_cpu(next);
+}
+
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+               struct exfat_node* node, uint32_t count)
+{
+       uint32_t i;
+
+       if (node->fptr_index > count)
+       {
+               node->fptr_index = 0;
+               node->fptr_cluster = node->start_cluster;
+       }
+
+       for (i = node->fptr_index; i < count; i++)
+       {
+               node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);
+               if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster))
+                       break; /* the caller should handle this and print appropriate 
+                                 error message */
+       }
+       node->fptr_index = count;
+       return node->fptr_cluster;
+}
+
+static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
+{
+       const size_t start_index = start / sizeof(bitmap_t) / 8;
+       const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8);
+       size_t i;
+       size_t start_bitindex;
+       size_t end_bitindex;
+       size_t c;
+
+       for (i = start_index; i < end_index; i++)
+       {
+               if (bitmap[i] == ~((bitmap_t) 0))
+                       continue;
+               start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start);
+               end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end);
+               for (c = start_bitindex; c < end_bitindex; c++)
+                       if (BMAP_GET(bitmap, c) == 0)
+                       {
+                               BMAP_SET(bitmap, c);
+                               return c + EXFAT_FIRST_DATA_CLUSTER;
+                       }
+       }
+       return EXFAT_CLUSTER_END;
+}
+
+static int flush_nodes(struct exfat* ef, struct exfat_node* node)
+{
+       struct exfat_node* p;
+
+       for (p = node->child; p != NULL; p = p->next)
+       {
+               int rc = flush_nodes(ef, p);
+               if (rc != 0)
+                       return rc;
+       }
+       return exfat_flush_node(ef, node);
+}
+
+int exfat_flush_nodes(struct exfat* ef)
+{
+       return flush_nodes(ef, ef->root);
+}
+
+int exfat_flush(struct exfat* ef)
+{
+       if (ef->cmap.dirty)
+       {
+               if (exfat_pwrite(ef->dev, ef->cmap.chunk,
+                               BMAP_SIZE(ef->cmap.chunk_size),
+                               exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+               {
+                       exfat_error("failed to write clusters bitmap");
+                       return -EIO;
+               }
+               ef->cmap.dirty = false;
+       }
+
+       return 0;
+}
+
+static bool set_next_cluster(const struct exfat* ef, bool contiguous,
+               cluster_t current, cluster_t next)
+{
+       off_t fat_offset;
+       le32_t next_le32;
+
+       if (contiguous)
+               return true;
+       fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+               + current * sizeof(cluster_t);
+       next_le32 = cpu_to_le32(next);
+       if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0)
+       {
+               exfat_error("failed to write the next cluster %#x after %#x", next,
+                               current);
+               return false;
+       }
+       return true;
+}
+
+static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint)
+{
+       cluster_t cluster;
+
+       hint -= EXFAT_FIRST_DATA_CLUSTER;
+       if (hint >= ef->cmap.chunk_size)
+               hint = 0;
+
+       cluster = find_bit_and_set(ef->cmap.chunk, hint, ef->cmap.chunk_size);
+       if (cluster == EXFAT_CLUSTER_END)
+               cluster = find_bit_and_set(ef->cmap.chunk, 0, hint);
+       if (cluster == EXFAT_CLUSTER_END)
+       {
+               exfat_error("no free space left");
+               return EXFAT_CLUSTER_END;
+       }
+
+       ef->cmap.dirty = true;
+       return cluster;
+}
+
+static void free_cluster(struct exfat* ef, cluster_t cluster)
+{
+       if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size)
+               exfat_bug("caller must check cluster validity (%#x, %#x)", cluster,
+                               ef->cmap.size);
+
+       BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER);
+       ef->cmap.dirty = true;
+}
+
+static bool make_noncontiguous(const struct exfat* ef, cluster_t first,
+               cluster_t last)
+{
+       cluster_t c;
+
+       for (c = first; c < last; c++)
+               if (!set_next_cluster(ef, false, c, c + 1))
+                       return false;
+       return true;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+               uint32_t current, uint32_t difference);
+
+static int grow_file(struct exfat* ef, struct exfat_node* node,
+               uint32_t current, uint32_t difference)
+{
+       cluster_t previous;
+       cluster_t next;
+       uint32_t allocated = 0;
+
+       if (difference == 0)
+               exfat_bug("zero clusters count passed");
+
+       if (node->start_cluster != EXFAT_CLUSTER_FREE)
+       {
+               /* get the last cluster of the file */
+               previous = exfat_advance_cluster(ef, node, current - 1);
+               if (CLUSTER_INVALID(*ef->sb, previous))
+               {
+                       exfat_error("invalid cluster 0x%x while growing", previous);
+                       return -EIO;
+               }
+       }
+       else
+       {
+               if (node->fptr_index != 0)
+                       exfat_bug("non-zero pointer index (%u)", node->fptr_index);
+               /* file does not have clusters (i.e. is empty), allocate
+                  the first one for it */
+               previous = allocate_cluster(ef, 0);
+               if (CLUSTER_INVALID(*ef->sb, previous))
+                       return -ENOSPC;
+               node->fptr_cluster = node->start_cluster = previous;
+               allocated = 1;
+               /* file consists of only one cluster, so it's contiguous */
+               node->is_contiguous = true;
+       }
+
+       while (allocated < difference)
+       {
+               next = allocate_cluster(ef, previous + 1);
+               if (CLUSTER_INVALID(*ef->sb, next))
+               {
+                       if (allocated != 0)
+                               shrink_file(ef, node, current + allocated, allocated);
+                       return -ENOSPC;
+               }
+               if (next != previous - 1 && node->is_contiguous)
+               {
+                       /* it's a pity, but we are not able to keep the file contiguous
+                          anymore */
+                       if (!make_noncontiguous(ef, node->start_cluster, previous))
+                               return -EIO;
+                       node->is_contiguous = false;
+                       node->is_dirty = true;
+               }
+               if (!set_next_cluster(ef, node->is_contiguous, previous, next))
+                       return -EIO;
+               previous = next;
+               allocated++;
+       }
+
+       if (!set_next_cluster(ef, node->is_contiguous, previous,
+                       EXFAT_CLUSTER_END))
+               return -EIO;
+       return 0;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+               uint32_t current, uint32_t difference)
+{
+       cluster_t previous;
+       cluster_t next;
+
+       if (difference == 0)
+               exfat_bug("zero difference passed");
+       if (node->start_cluster == EXFAT_CLUSTER_FREE)
+               exfat_bug("unable to shrink empty file (%u clusters)", current);
+       if (current < difference)
+               exfat_bug("file underflow (%u < %u)", current, difference);
+
+       /* crop the file */
+       if (current > difference)
+       {
+               cluster_t last = exfat_advance_cluster(ef, node,
+                               current - difference - 1);
+               if (CLUSTER_INVALID(*ef->sb, last))
+               {
+                       exfat_error("invalid cluster 0x%x while shrinking", last);
+                       return -EIO;
+               }
+               previous = exfat_next_cluster(ef, node, last);
+               if (!set_next_cluster(ef, node->is_contiguous, last,
+                               EXFAT_CLUSTER_END))
+                       return -EIO;
+       }
+       else
+       {
+               previous = node->start_cluster;
+               node->start_cluster = EXFAT_CLUSTER_FREE;
+               node->is_dirty = true;
+       }
+       node->fptr_index = 0;
+       node->fptr_cluster = node->start_cluster;
+
+       /* free remaining clusters */
+       while (difference--)
+       {
+               if (CLUSTER_INVALID(*ef->sb, previous))
+               {
+                       exfat_error("invalid cluster 0x%x while freeing after shrink",
+                                       previous);
+                       return -EIO;
+               }
+
+               next = exfat_next_cluster(ef, node, previous);
+               if (!set_next_cluster(ef, node->is_contiguous, previous,
+                               EXFAT_CLUSTER_FREE))
+                       return -EIO;
+               free_cluster(ef, previous);
+               previous = next;
+       }
+       return 0;
+}
+
+static bool erase_raw(struct exfat* ef, size_t size, off_t offset)
+{
+       if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0)
+       {
+               exfat_error("failed to erase %zu bytes at %"PRId64, size, offset);
+               return false;
+       }
+       return true;
+}
+
+static int erase_range(struct exfat* ef, struct exfat_node* node,
+               uint64_t begin, uint64_t end)
+{
+       uint64_t cluster_boundary;
+       cluster_t cluster;
+
+       if (begin >= end)
+               return 0;
+
+       cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1;
+       cluster = exfat_advance_cluster(ef, node,
+                       begin / CLUSTER_SIZE(*ef->sb));
+       if (CLUSTER_INVALID(*ef->sb, cluster))
+       {
+               exfat_error("invalid cluster 0x%x while erasing", cluster);
+               return -EIO;
+       }
+       /* erase from the beginning to the closest cluster boundary */
+       if (!erase_raw(ef, MIN(cluster_boundary, end) - begin,
+                       exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb)))
+               return -EIO;
+       /* erase whole clusters */
+       while (cluster_boundary < end)
+       {
+               cluster = exfat_next_cluster(ef, node, cluster);
+               /* the cluster cannot be invalid because we have just allocated it */
+               if (CLUSTER_INVALID(*ef->sb, cluster))
+                       exfat_bug("invalid cluster 0x%x after allocation", cluster);
+               if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster)))
+                       return -EIO;
+               cluster_boundary += CLUSTER_SIZE(*ef->sb);
+       }
+       return 0;
+}
+
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+               bool erase)
+{
+       uint32_t c1 = bytes2clusters(ef, node->size);
+       uint32_t c2 = bytes2clusters(ef, size);
+       int rc = 0;
+
+       if (node->references == 0 && node->parent)
+               exfat_bug("no references, node changes can be lost");
+
+       if (node->size == size)
+               return 0;
+
+       if (c1 < c2)
+               rc = grow_file(ef, node, c1, c2 - c1);
+       else if (c1 > c2)
+               rc = shrink_file(ef, node, c1, c1 - c2);
+
+       if (rc != 0)
+               return rc;
+
+       if (erase)
+       {
+               rc = erase_range(ef, node, node->size, size);
+               if (rc != 0)
+                       return rc;
+       }
+
+       exfat_update_mtime(node);
+       node->size = size;
+       node->is_dirty = true;
+       return 0;
+}
+
+uint32_t exfat_count_free_clusters(const struct exfat* ef)
+{
+       uint32_t free_clusters = 0;
+       uint32_t i;
+
+       for (i = 0; i < ef->cmap.size; i++)
+               if (BMAP_GET(ef->cmap.chunk, i) == 0)
+                       free_clusters++;
+       return free_clusters;
+}
+
+static int find_used_clusters(const struct exfat* ef,
+               cluster_t* a, cluster_t* b)
+{
+       const cluster_t end = le32_to_cpu(ef->sb->cluster_count);
+
+       /* find first used cluster */
+       for (*a = *b + 1; *a < end; (*a)++)
+               if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER))
+                       break;
+       if (*a >= end)
+               return 1;
+
+       /* find last contiguous used cluster */
+       for (*b = *a; *b < end; (*b)++)
+               if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0)
+               {
+                       (*b)--;
+                       break;
+               }
+
+       return 0;
+}
+
+int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b)
+{
+       cluster_t ca, cb;
+
+       if (*a == 0 && *b == 0)
+               ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1;
+       else
+       {
+               ca = s2c(ef, *a);
+               cb = s2c(ef, *b);
+       }
+       if (find_used_clusters(ef, &ca, &cb) != 0)
+               return 1;
+       if (*a != 0 || *b != 0)
+               *a = c2s(ef, ca);
+       *b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb);
+       return 0;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h
new file mode 100644 (file)
index 0000000..7eb686c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+       compiler.h (09.06.13)
+       Compiler-specific definitions. Note that unknown compiler is not a
+       showstopper.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef COMPILER_H_INCLUDED
+#define COMPILER_H_INCLUDED
+
+#if __STDC_VERSION__ < 199901L
+#error C99-compliant compiler is required
+#endif
+
+#if defined(__clang__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __has_extension(c_static_assert)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#elif defined(__GNUC__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#else
+
+#define PRINTF
+#define NORETURN
+#define PACKED
+
+#endif
+
+#ifdef USE_C11_STATIC_ASSERT
+#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
+#else
+#define CONCAT2(a, b) a ## b
+#define CONCAT1(a, b) CONCAT2(a, b)
+#define STATIC_ASSERT(cond) \
+       extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1])
+#endif
+
+#endif /* ifndef COMPILER_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h
new file mode 100644 (file)
index 0000000..73059a7
--- /dev/null
@@ -0,0 +1,40 @@
+/* libexfat/config.h.  Generated from config.h.in by configure.  */
+/* libexfat/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Name of package */
+#define PACKAGE "exfat"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "relan@users.noreply.github.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "Free exFAT implementation"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "Free exFAT implementation 1.3.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "exfat"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "https://github.com/relan/exfat"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.3.0"
+
+/* Define if block devices are not supported. */
+/* #undef USE_UBLIO */
+
+/* Version number of package */
+#define VERSION "1.3.0"
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h
new file mode 100644 (file)
index 0000000..b3ff78d
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+       exfat.h (29.08.09)
+       Definitions of structures and constants used in exFAT file system
+       implementation.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFAT_H_INCLUDED
+#define EXFAT_H_INCLUDED
+
+#ifndef ANDROID
+/* Android.bp is used instead of autotools when targeting Android */
+#include "config.h"
+#endif
+#include "compiler.h"
+#include "exfatfs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define EXFAT_NAME_MAX 255
+/* UTF-16 encodes code points up to U+FFFF as single 16-bit code units.
+   UTF-8 uses up to 3 bytes (i.e. 8-bit code units) to encode code points
+   up to U+FFFF. One additional character is for null terminator. */
+#define EXFAT_UTF8_NAME_BUFFER_MAX (EXFAT_NAME_MAX * 3 + 1)
+#define EXFAT_UTF8_ENAME_BUFFER_MAX (EXFAT_ENAME_MAX * 3 + 1)
+
+#define SECTOR_SIZE(sb) (1 << (sb).sector_bits)
+#define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits)
+#define CLUSTER_INVALID(sb, c) ((c) < EXFAT_FIRST_DATA_CLUSTER || \
+       (c) - EXFAT_FIRST_DATA_CLUSTER >= le32_to_cpu((sb).cluster_count))
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d))
+#define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
+
+#define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8)
+#define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8)
+#define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8)))
+#define BMAP_GET(bitmap, index) \
+       ((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index))
+#define BMAP_SET(bitmap, index) \
+       ((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index))
+#define BMAP_CLR(bitmap, index) \
+       ((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index))
+
+#define EXFAT_REPAIR(hook, ef, ...) \
+       (exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__))
+
+/* The size of off_t type must be 64 bits. File systems larger than 2 GB will
+   be corrupted with 32-bit off_t. */
+STATIC_ASSERT(sizeof(off_t) == 8);
+
+struct exfat_node
+{
+       struct exfat_node* parent;
+       struct exfat_node* child;
+       struct exfat_node* next;
+       struct exfat_node* prev;
+
+       int references;
+       uint32_t fptr_index;
+       cluster_t fptr_cluster;
+       off_t entry_offset;
+       cluster_t start_cluster;
+       uint16_t attrib;
+       uint8_t continuations;
+       bool is_contiguous : 1;
+       bool is_cached : 1;
+       bool is_dirty : 1;
+       bool is_unlinked : 1;
+       uint64_t size;
+       time_t mtime, atime;
+       le16_t name[EXFAT_NAME_MAX + 1];
+};
+
+enum exfat_mode
+{
+       EXFAT_MODE_RO,
+       EXFAT_MODE_RW,
+       EXFAT_MODE_ANY,
+};
+
+struct exfat_dev;
+
+struct exfat
+{
+       struct exfat_dev* dev;
+       struct exfat_super_block* sb;
+       uint16_t* upcase;
+       struct exfat_node* root;
+       struct
+       {
+               cluster_t start_cluster;
+               uint32_t size;                          /* in bits */
+               bitmap_t* chunk;
+               uint32_t chunk_size;            /* in bits */
+               bool dirty;
+       }
+       cmap;
+       char label[EXFAT_UTF8_ENAME_BUFFER_MAX];
+       void* zero_cluster;
+       int dmask, fmask;
+       uid_t uid;
+       gid_t gid;
+       int ro;
+       bool noatime;
+       enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair;
+};
+
+/* in-core nodes iterator */
+struct exfat_iterator
+{
+       struct exfat_node* parent;
+       struct exfat_node* current;
+};
+
+struct exfat_human_bytes
+{
+       uint64_t value;
+       const char* unit;
+};
+
+extern int exfat_errors;
+extern int exfat_errors_fixed;
+
+#define VLOG_LOG    1
+#define VLOG_DEBUG  2
+void ventoy_syslog_newline(int level, const char *Fmt, ...);
+#define exfat_bug(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_warn(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
+#define exfat_debug(fmt, args...) ventoy_syslog_newline(VLOG_DEBUG, fmt, ##args)
+
+#if 0
+void exfat_bug(const char* format, ...) PRINTF NORETURN;
+void exfat_error(const char* format, ...) PRINTF;
+void exfat_warn(const char* format, ...) PRINTF;
+void exfat_debug(const char* format, ...) PRINTF;
+#endif /* #if 0 */
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode);
+int exfat_close(struct exfat_dev* dev);
+int exfat_fsync(struct exfat_dev* dev);
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev);
+off_t exfat_get_size(const struct exfat_dev* dev);
+off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence);
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size);
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size);
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+               off_t offset);
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+               off_t offset);
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+               void* buffer, size_t size, off_t offset);
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+               const void* buffer, size_t size, off_t offset);
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+               struct exfat_iterator* it);
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it);
+struct exfat_node* exfat_readdir(struct exfat_iterator* it);
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+               const char* path);
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+               struct exfat_node** node, le16_t* name, const char* path);
+
+off_t exfat_c2o(const struct exfat* ef, cluster_t cluster);
+cluster_t exfat_next_cluster(const struct exfat* ef,
+               const struct exfat_node* node, cluster_t cluster);
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+               struct exfat_node* node, uint32_t count);
+int exfat_flush_nodes(struct exfat* ef);
+int exfat_flush(struct exfat* ef);
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+               bool erase);
+uint32_t exfat_count_free_clusters(const struct exfat* ef);
+int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b);
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+               struct stat* stbuf);
+void exfat_get_name(const struct exfat_node* node,
+               char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]);
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry);
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum);
+le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n);
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size);
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum);
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
+               size_t length);
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb);
+void exfat_print_info(const struct exfat_super_block* sb,
+               uint32_t free_clusters);
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+               size_t insize);
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+               size_t insize);
+size_t utf16_length(const le16_t* str);
+
+struct exfat_node* exfat_get_node(struct exfat_node* node);
+void exfat_put_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir);
+void exfat_reset_cache(struct exfat* ef);
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node);
+int exfat_unlink(struct exfat* ef, struct exfat_node* node);
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node);
+int exfat_mknod(struct exfat* ef, const char* path);
+int exfat_mkdir(struct exfat* ef, const char* path);
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path);
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]);
+void exfat_update_atime(struct exfat_node* node);
+void exfat_update_mtime(struct exfat_node* node);
+const char* exfat_get_label(struct exfat* ef);
+int exfat_set_label(struct exfat* ef, const char* label);
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options);
+void exfat_unmount(struct exfat* ef);
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec);
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+               uint8_t* centisec);
+void exfat_tzset(void);
+
+bool exfat_ask_to_fix(const struct exfat* ef);
+bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
+               uint32_t vbr_checksum);
+bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
+               struct exfat_node* node);
+bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
+               const struct exfat_entry* entry, off_t offset);
+
+#endif /* ifndef EXFAT_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h
new file mode 100644 (file)
index 0000000..b7b6cac
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+       exfatfs.h (29.08.09)
+       Definitions of structures and constants used in exFAT file system.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFATFS_H_INCLUDED
+#define EXFATFS_H_INCLUDED
+
+#include "byteorder.h"
+#include "compiler.h"
+
+typedef uint32_t cluster_t;            /* cluster number */
+
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xfffffff6
+
+#define EXFAT_CLUSTER_FREE         0 /* free cluster */
+#define EXFAT_CLUSTER_BAD 0xfffffff7 /* cluster contains bad sector */
+#define EXFAT_CLUSTER_END 0xffffffff /* final cluster of file or directory */
+
+#define EXFAT_STATE_MOUNTED 2
+
+struct exfat_super_block
+{
+       uint8_t jump[3];                                /* 0x00 jmp and nop instructions */
+       uint8_t oem_name[8];                    /* 0x03 "EXFAT   " */
+       uint8_t __unused1[53];                  /* 0x0B always 0 */
+       le64_t sector_start;                    /* 0x40 partition first sector */
+       le64_t sector_count;                    /* 0x48 partition sectors count */
+       le32_t fat_sector_start;                /* 0x50 FAT first sector */
+       le32_t fat_sector_count;                /* 0x54 FAT sectors count */
+       le32_t cluster_sector_start;    /* 0x58 first cluster sector */
+       le32_t cluster_count;                   /* 0x5C total clusters count */
+       le32_t rootdir_cluster;                 /* 0x60 first cluster of the root dir */
+       le32_t volume_serial;                   /* 0x64 volume serial number */
+       struct                                                  /* 0x68 FS version */
+       {
+               uint8_t minor;
+               uint8_t major;
+       }
+       version;
+       le16_t volume_state;                    /* 0x6A volume state flags */
+       uint8_t sector_bits;                    /* 0x6C sector size as (1 << n) */
+       uint8_t spc_bits;                               /* 0x6D sectors per cluster as (1 << n) */
+       uint8_t fat_count;                              /* 0x6E always 1 */
+       uint8_t drive_no;                               /* 0x6F always 0x80 */
+       uint8_t allocated_percent;              /* 0x70 percentage of allocated space */
+       uint8_t __unused2[397];                 /* 0x71 always 0 */
+       le16_t boot_signature;                  /* the value of 0xAA55 */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_super_block) == 512);
+
+#define EXFAT_ENTRY_VALID     0x80
+#define EXFAT_ENTRY_CONTINUED 0x40
+#define EXFAT_ENTRY_OPTIONAL  0x20
+
+#define EXFAT_ENTRY_BITMAP    (0x01 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_UPCASE    (0x02 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_LABEL     (0x03 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE      (0x05 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE_INFO (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+#define EXFAT_ENTRY_FILE_NAME (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+#define EXFAT_ENTRY_FILE_TAIL (0x00 | EXFAT_ENTRY_VALID \
+                                    | EXFAT_ENTRY_CONTINUED \
+                                    | EXFAT_ENTRY_OPTIONAL)
+
+struct exfat_entry                                     /* common container for all entries */
+{
+       uint8_t type;                                   /* any of EXFAT_ENTRY_xxx */
+       uint8_t data[31];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry) == 32);
+
+#define EXFAT_ENAME_MAX 15
+
+struct exfat_entry_bitmap                      /* allocated clusters bitmap */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_BITMAP */
+       uint8_t __unknown1[19];
+       le32_t start_cluster;
+       le64_t size;                                    /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);
+
+#define EXFAT_UPCASE_CHARS 0x10000
+
+struct exfat_entry_upcase                      /* upper case translation table */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_UPCASE */
+       uint8_t __unknown1[3];
+       le32_t checksum;
+       uint8_t __unknown2[12];
+       le32_t start_cluster;
+       le64_t size;                                    /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32);
+
+struct exfat_entry_label                       /* volume label */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_LABEL */
+       uint8_t length;                                 /* number of characters */
+       le16_t name[EXFAT_ENAME_MAX];   /* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_label) == 32);
+
+#define EXFAT_ATTRIB_RO     0x01
+#define EXFAT_ATTRIB_HIDDEN 0x02
+#define EXFAT_ATTRIB_SYSTEM 0x04
+#define EXFAT_ATTRIB_VOLUME 0x08
+#define EXFAT_ATTRIB_DIR    0x10
+#define EXFAT_ATTRIB_ARCH   0x20
+
+struct exfat_entry_meta1                       /* file or directory info (part 1) */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_FILE */
+       uint8_t continuations;
+       le16_t checksum;
+       le16_t attrib;                                  /* combination of EXFAT_ATTRIB_xxx */
+       le16_t __unknown1;
+       le16_t crtime, crdate;                  /* creation date and time */
+       le16_t mtime, mdate;                    /* latest modification date and time */
+       le16_t atime, adate;                    /* latest access date and time */
+       uint8_t crtime_cs;                              /* creation time in cs (centiseconds) */
+       uint8_t mtime_cs;                               /* latest modification time in cs */
+       uint8_t __unknown2[10];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);
+
+#define EXFAT_FLAG_ALWAYS1             (1u << 0)
+#define EXFAT_FLAG_CONTIGUOUS  (1u << 1)
+
+struct exfat_entry_meta2                       /* file or directory info (part 2) */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_FILE_INFO */
+       uint8_t flags;                                  /* combination of EXFAT_FLAG_xxx */
+       uint8_t __unknown1;
+       uint8_t name_length;
+       le16_t name_hash;
+       le16_t __unknown2;
+       le64_t valid_size;                              /* in bytes, less or equal to size */
+       uint8_t __unknown3[4];
+       le32_t start_cluster;
+       le64_t size;                                    /* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32);
+
+struct exfat_entry_name                                /* file or directory name */
+{
+       uint8_t type;                                   /* EXFAT_ENTRY_FILE_NAME */
+       uint8_t __unknown;
+       le16_t name[EXFAT_ENAME_MAX];   /* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_name) == 32);
+
+#endif /* ifndef EXFATFS_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c
new file mode 100644 (file)
index 0000000..4dee536
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+       io.c (02.09.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#if defined(__APPLE__)
+#include <sys/disk.h>
+#elif defined(__OpenBSD__)
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/ioctl.h>
+#elif __linux__
+#include <sys/mount.h>
+#endif
+#ifdef USE_UBLIO
+#include <sys/uio.h>
+#include <ublio.h>
+#endif
+
+struct exfat_dev
+{
+       int fd;
+       enum exfat_mode mode;
+       off_t size; /* in bytes */
+#ifdef USE_UBLIO
+       off_t pos;
+       ublio_filehandle_t ufh;
+#endif
+};
+
+int g_vtoy_exfat_disk_fd = -1;
+uint64_t g_vtoy_exfat_part_size = 0;
+
+static bool is_open(int fd)
+{
+       return fcntl(fd, F_GETFD) != -1;
+}
+
+static int open_ro(const char* spec)
+{
+       return open(spec, O_RDONLY);
+}
+
+static int open_rw(const char* spec)
+{
+       int fd = open(spec, O_RDWR);
+#ifdef __linux__
+       int ro = 0;
+
+       /*
+          This ioctl is needed because after "blockdev --setro" kernel still
+          allows to open the device in read-write mode but fails writes.
+       */
+       if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
+       {
+               close(fd);
+               errno = EROFS;
+               return -1;
+       }
+#endif
+       return fd;
+}
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
+{
+       struct exfat_dev* dev;
+       struct stat stbuf;
+#ifdef USE_UBLIO
+       struct ublio_param up;
+#endif
+
+       /* The system allocates file descriptors sequentially. If we have been
+          started with stdin (0), stdout (1) or stderr (2) closed, the system
+          will give us descriptor 0, 1 or 2 later when we open block device,
+          FUSE communication pipe, etc. As a result, functions using stdin,
+          stdout or stderr will actually work with a different thing and can
+          corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */
+       while (!is_open(STDIN_FILENO)
+               || !is_open(STDOUT_FILENO)
+               || !is_open(STDERR_FILENO))
+       {
+               /* we don't need those descriptors, let them leak */
+               if (open("/dev/null", O_RDWR) == -1)
+               {
+                       exfat_error("failed to open /dev/null");
+                       return NULL;
+               }
+       }
+
+       dev = malloc(sizeof(struct exfat_dev));
+       if (dev == NULL)
+       {
+               exfat_error("failed to allocate memory for device structure");
+               return NULL;
+       }
+
+       switch (mode)
+       {
+       case EXFAT_MODE_RO:
+               dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
+               if (dev->fd == -1)
+               {
+                       free(dev);
+                       exfat_error("failed to open '%s' in read-only mode: %s", spec,
+                                       strerror(errno));
+                       return NULL;
+               }
+               dev->mode = EXFAT_MODE_RO;
+               break;
+       case EXFAT_MODE_RW:
+               dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
+               if (dev->fd == -1)
+               {
+                       free(dev);
+                       exfat_error("failed to open '%s' in read-write mode: %s", spec,
+                                       strerror(errno));
+                       return NULL;
+               }
+               dev->mode = EXFAT_MODE_RW;
+               break;
+       case EXFAT_MODE_ANY:
+               dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
+               if (dev->fd != -1)
+               {
+                       dev->mode = EXFAT_MODE_RW;
+                       break;
+               }
+               dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
+               if (dev->fd != -1)
+               {
+                       dev->mode = EXFAT_MODE_RO;
+                       exfat_warn("'%s' is write-protected, mounting read-only", spec);
+                       break;
+               }
+               free(dev);
+               exfat_error("failed to open '%s': %s", spec, strerror(errno));
+               return NULL;
+       }
+
+       if (fstat(dev->fd, &stbuf) != 0)
+       {
+               close(dev->fd);
+               free(dev);
+               exfat_error("failed to fstat '%s'", spec);
+               return NULL;
+       }
+       if (!S_ISBLK(stbuf.st_mode) &&
+               !S_ISCHR(stbuf.st_mode) &&
+               !S_ISREG(stbuf.st_mode))
+       {
+               close(dev->fd);
+               free(dev);
+               exfat_error("'%s' is neither a device, nor a regular file", spec);
+               return NULL;
+       }
+
+#if defined(__APPLE__)
+       if (!S_ISREG(stbuf.st_mode))
+       {
+               uint32_t block_size = 0;
+               uint64_t blocks = 0;
+
+               if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to get block size");
+                       return NULL;
+               }
+               if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to get blocks count");
+                       return NULL;
+               }
+               dev->size = blocks * block_size;
+       }
+       else
+#elif defined(__OpenBSD__)
+       if (!S_ISREG(stbuf.st_mode))
+       {
+               struct disklabel lab;
+               struct partition* pp;
+               char* partition;
+
+               if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to get disklabel");
+                       return NULL;
+               }
+
+               /* Don't need to check that partition letter is valid as we won't get
+                  this far otherwise. */
+               partition = strchr(spec, '\0') - 1;
+               pp = &(lab.d_partitions[*partition - 'a']);
+               dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
+
+               if (pp->p_fstype != FS_NTFS)
+                       exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
+                                       "you can fix this with fdisk(8)");
+       }
+       else
+#endif
+       {
+               /* works for Linux, FreeBSD, Solaris */
+               dev->size = exfat_seek(dev, 0, SEEK_END);
+               if (dev->size <= 0)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to get size of '%s'", spec);
+                       return NULL;
+               }
+               if (exfat_seek(dev, 0, SEEK_SET) == -1)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to seek to the beginning of '%s'", spec);
+                       return NULL;
+               }
+       }
+
+#ifdef USE_UBLIO
+       memset(&up, 0, sizeof(struct ublio_param));
+       up.up_blocksize = 256 * 1024;
+       up.up_items = 64;
+       up.up_grace = 32;
+       up.up_priv = &dev->fd;
+
+       dev->pos = 0;
+       dev->ufh = ublio_open(&up);
+       if (dev->ufh == NULL)
+       {
+               close(dev->fd);
+               free(dev);
+               exfat_error("failed to initialize ublio");
+               return NULL;
+       }
+#endif
+
+       return dev;
+}
+
+int exfat_close(struct exfat_dev* dev)
+{
+       int rc = 0;
+
+#ifdef USE_UBLIO
+       if (ublio_close(dev->ufh) != 0)
+       {
+               exfat_error("failed to close ublio");
+               rc = -EIO;
+       }
+#endif
+    if (dev->fd != g_vtoy_exfat_disk_fd)
+    {
+        if (close(dev->fd) != 0)
+       {
+               exfat_error("failed to close device: %s", strerror(errno));
+               rc = -EIO;
+       }
+    }
+       
+       free(dev);
+       return rc;
+}
+
+int exfat_fsync(struct exfat_dev* dev)
+{
+       int rc = 0;
+
+#ifdef USE_UBLIO
+       if (ublio_fsync(dev->ufh) != 0)
+       {
+               exfat_error("ublio fsync failed");
+               rc = -EIO;
+       }
+#endif
+       if (fsync(dev->fd) != 0)
+       {
+               exfat_error("fsync failed: %s", strerror(errno));
+               rc = -EIO;
+       }
+       return rc;
+}
+
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
+{
+       return dev->mode;
+}
+
+
+off_t exfat_get_size(const struct exfat_dev* dev)
+{
+       return dev->size;
+}
+
+off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
+{
+#ifdef USE_UBLIO
+       /* XXX SEEK_CUR will be handled incorrectly */
+       return dev->pos = lseek(dev->fd, offset, whence);
+#else
+
+    if (SEEK_SET == whence)
+    {
+        if (offset > g_vtoy_exfat_part_size)
+        {
+            return -1;
+        }
+        
+        lseek(dev->fd, 512 * 2048 + offset, SEEK_SET);
+        return offset;
+    }
+    else if (SEEK_END == whence)
+    {
+        if (offset == 0)
+        {
+            offset = 512 * 2048 + g_vtoy_exfat_part_size;
+            lseek(dev->fd, offset, SEEK_SET);
+            return (off_t)g_vtoy_exfat_part_size;
+        }
+        else
+        {
+            exfat_error("Invalid SEEK_END offset %llu", (unsigned long long)offset);
+            return -1;
+        }
+    }
+    else
+    {
+        exfat_error("Invalid seek whence %d", whence);
+       return lseek(dev->fd, offset, whence);        
+    }
+#endif
+}
+
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+       ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
+       if (result >= 0)
+               dev->pos += size;
+       return result;
+#else
+       return read(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+       ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
+       if (result >= 0)
+               dev->pos += size;
+       return result;
+#else
+       return write(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+               off_t offset)
+{
+#ifdef USE_UBLIO
+       return ublio_pread(dev->ufh, buffer, size, offset);
+#else
+       return pread(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+               off_t offset)
+{
+#ifdef USE_UBLIO
+       return ublio_pwrite(dev->ufh, buffer, size, offset);
+#else
+       return pwrite(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+               void* buffer, size_t size, off_t offset)
+{
+       cluster_t cluster;
+       char* bufp = buffer;
+       off_t lsize, loffset, remainder;
+
+       if (offset >= node->size)
+               return 0;
+       if (size == 0)
+               return 0;
+
+       cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+       if (CLUSTER_INVALID(*ef->sb, cluster))
+       {
+               exfat_error("invalid cluster 0x%x while reading", cluster);
+               return -EIO;
+       }
+
+       loffset = offset % CLUSTER_SIZE(*ef->sb);
+       remainder = MIN(size, node->size - offset);
+       while (remainder > 0)
+       {
+               if (CLUSTER_INVALID(*ef->sb, cluster))
+               {
+                       exfat_error("invalid cluster 0x%x while reading", cluster);
+                       return -EIO;
+               }
+               lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+               if (exfat_pread(ef->dev, bufp, lsize,
+                                       exfat_c2o(ef, cluster) + loffset) < 0)
+               {
+                       exfat_error("failed to read cluster %#x", cluster);
+                       return -EIO;
+               }
+               bufp += lsize;
+               loffset = 0;
+               remainder -= lsize;
+               cluster = exfat_next_cluster(ef, node, cluster);
+       }
+       if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
+               exfat_update_atime(node);
+       return MIN(size, node->size - offset) - remainder;
+}
+
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+               const void* buffer, size_t size, off_t offset)
+{
+       int rc;
+       cluster_t cluster;
+       const char* bufp = buffer;
+       off_t lsize, loffset, remainder;
+
+       if (offset > node->size)
+       {
+               rc = exfat_truncate(ef, node, offset, true);
+               if (rc != 0)
+                       return rc;
+       }
+       if (offset + size > node->size)
+       {
+               rc = exfat_truncate(ef, node, offset + size, false);
+               if (rc != 0)
+                       return rc;
+       }
+       if (size == 0)
+               return 0;
+
+       cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+       if (CLUSTER_INVALID(*ef->sb, cluster))
+       {
+               exfat_error("invalid cluster 0x%x while writing", cluster);
+               return -EIO;
+       }
+
+       loffset = offset % CLUSTER_SIZE(*ef->sb);
+       remainder = size;
+       while (remainder > 0)
+       {
+               if (CLUSTER_INVALID(*ef->sb, cluster))
+               {
+                       exfat_error("invalid cluster 0x%x while writing", cluster);
+                       return -EIO;
+               }
+               lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+               if (exfat_pwrite(ef->dev, bufp, lsize,
+                               exfat_c2o(ef, cluster) + loffset) < 0)
+               {
+                       exfat_error("failed to write cluster %#x", cluster);
+                       return -EIO;
+               }
+               bufp += lsize;
+               loffset = 0;
+               remainder -= lsize;
+               cluster = exfat_next_cluster(ef, node, cluster);
+       }
+       if (!(node->attrib & EXFAT_ATTRIB_DIR))
+               /* directory's mtime should be updated by the caller only when it
+                  creates or removes something in this directory */
+               exfat_update_mtime(node);
+       return size - remainder;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c
new file mode 100644 (file)
index 0000000..8fa8e0f
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+       lookup.c (02.09.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+               struct exfat_iterator* it)
+{
+       int rc;
+
+       exfat_get_node(dir);
+       it->parent = dir;
+       it->current = NULL;
+       rc = exfat_cache_directory(ef, dir);
+       if (rc != 0)
+               exfat_put_node(ef, dir);
+       return rc;
+}
+
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it)
+{
+       exfat_put_node(ef, it->parent);
+       it->parent = NULL;
+       it->current = NULL;
+}
+
+struct exfat_node* exfat_readdir(struct exfat_iterator* it)
+{
+       if (it->current == NULL)
+               it->current = it->parent->child;
+       else
+               it->current = it->current->next;
+
+       if (it->current != NULL)
+               return exfat_get_node(it->current);
+       else
+               return NULL;
+}
+
+static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
+{
+       return (int) ef->upcase[a] - (int) ef->upcase[b];
+}
+
+static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
+{
+       while (le16_to_cpu(*a) && le16_to_cpu(*b))
+       {
+               int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+               if (rc != 0)
+                       return rc;
+               a++;
+               b++;
+       }
+       return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+}
+
+static int lookup_name(struct exfat* ef, struct exfat_node* parent,
+               struct exfat_node** node, const char* name, size_t n)
+{
+       struct exfat_iterator it;
+       le16_t buffer[EXFAT_NAME_MAX + 1];
+       int rc;
+
+       *node = NULL;
+
+       rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX + 1, n);
+       if (rc != 0)
+               return rc;
+
+       rc = exfat_opendir(ef, parent, &it);
+       if (rc != 0)
+               return rc;
+       while ((*node = exfat_readdir(&it)))
+       {
+               if (compare_name(ef, buffer, (*node)->name) == 0)
+               {
+                       exfat_closedir(ef, &it);
+                       return 0;
+               }
+               exfat_put_node(ef, *node);
+       }
+       exfat_closedir(ef, &it);
+       return -ENOENT;
+}
+
+static size_t get_comp(const char* path, const char** comp)
+{
+       const char* end;
+
+       *comp = path + strspn(path, "/");                               /* skip leading slashes */
+       end = strchr(*comp, '/');
+       if (end == NULL)
+               return strlen(*comp);
+       else
+               return end - *comp;
+}
+
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+               const char* path)
+{
+       struct exfat_node* parent;
+       const char* p;
+       size_t n;
+       int rc;
+
+       /* start from the root directory */
+       parent = *node = exfat_get_node(ef->root);
+       for (p = path; (n = get_comp(p, &p)); p += n)
+       {
+               if (n == 1 && *p == '.')                                /* skip "." component */
+                       continue;
+               rc = lookup_name(ef, parent, node, p, n);
+               if (rc != 0)
+               {
+                       exfat_put_node(ef, parent);
+                       return rc;
+               }
+               exfat_put_node(ef, parent);
+               parent = *node;
+       }
+       return 0;
+}
+
+static bool is_last_comp(const char* comp, size_t length)
+{
+       const char* p = comp + length;
+
+       return get_comp(p, &p) == 0;
+}
+
+static bool is_allowed(const char* comp, size_t length)
+{
+       size_t i;
+
+       for (i = 0; i < length; i++)
+               switch (comp[i])
+               {
+               case 0x01 ... 0x1f:
+               case '/':
+               case '\\':
+               case ':':
+               case '*':
+               case '?':
+               case '"':
+               case '<':
+               case '>':
+               case '|':
+                       return false;
+               }
+       return true;
+}
+
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+               struct exfat_node** node, le16_t* name, const char* path)
+{
+       const char* p;
+       size_t n;
+       int rc;
+
+       memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+       *parent = *node = exfat_get_node(ef->root);
+       for (p = path; (n = get_comp(p, &p)); p += n)
+       {
+               if (n == 1 && *p == '.')
+                       continue;
+               if (is_last_comp(p, n))
+               {
+                       if (!is_allowed(p, n))
+                       {
+                               /* contains characters that are not allowed */
+                               exfat_put_node(ef, *parent);
+                               return -ENOENT;
+                       }
+                       rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX + 1, n);
+                       if (rc != 0)
+                       {
+                               exfat_put_node(ef, *parent);
+                               return rc;
+                       }
+
+                       rc = lookup_name(ef, *parent, node, p, n);
+                       if (rc != 0 && rc != -ENOENT)
+                       {
+                               exfat_put_node(ef, *parent);
+                               return rc;
+                       }
+                       return 0;
+               }
+               rc = lookup_name(ef, *parent, node, p, n);
+               if (rc != 0)
+               {
+                       exfat_put_node(ef, *parent);
+                       return rc;
+               }
+               exfat_put_node(ef, *parent);
+               *parent = *node;
+       }
+       exfat_bug("impossible");
+
+    return 1;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c
new file mode 100644 (file)
index 0000000..4284aee
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+       mount.c (22.10.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+static uint64_t rootdir_size(const struct exfat* ef)
+{
+       uint32_t clusters = 0;
+       uint32_t clusters_max = le32_to_cpu(ef->sb->cluster_count);
+       cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+
+       /* Iterate all clusters of the root directory to calculate its size.
+          It can't be contiguous because there is no flag to indicate this. */
+       do
+       {
+               if (clusters == clusters_max) /* infinite loop detected */
+               {
+                       exfat_error("root directory cannot occupy all %d clusters",
+                                       clusters);
+                       return 0;
+               }
+               if (CLUSTER_INVALID(*ef->sb, rootdir_cluster))
+               {
+                       exfat_error("bad cluster %#x while reading root directory",
+                                       rootdir_cluster);
+                       return 0;
+               }
+               rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
+               clusters++;
+       }
+       while (rootdir_cluster != EXFAT_CLUSTER_END);
+
+       return (uint64_t) clusters * CLUSTER_SIZE(*ef->sb);
+}
+
+static const char* get_option(const char* options, const char* option_name)
+{
+       const char* p;
+       size_t length = strlen(option_name);
+
+       for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+               if ((p == options || p[-1] == ',') && p[length] == '=')
+                       return p + length + 1;
+       return NULL;
+}
+
+static int get_int_option(const char* options, const char* option_name,
+               int base, int default_value)
+{
+       const char* p = get_option(options, option_name);
+
+       if (p == NULL)
+               return default_value;
+       return strtol(p, NULL, base);
+}
+
+static bool match_option(const char* options, const char* option_name)
+{
+       const char* p;
+       size_t length = strlen(option_name);
+
+       for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+               if ((p == options || p[-1] == ',') &&
+                               (p[length] == ',' || p[length] == '\0'))
+                       return true;
+       return false;
+}
+
+static void parse_options(struct exfat* ef, const char* options)
+{
+       int opt_umask;
+
+       opt_umask = get_int_option(options, "umask", 8, 0);
+       ef->dmask = get_int_option(options, "dmask", 8, opt_umask);
+       ef->fmask = get_int_option(options, "fmask", 8, opt_umask);
+
+       ef->uid = get_int_option(options, "uid", 10, geteuid());
+       ef->gid = get_int_option(options, "gid", 10, getegid());
+
+       ef->noatime = match_option(options, "noatime");
+
+       switch (get_int_option(options, "repair", 10, 0))
+       {
+       case 1:
+               ef->repair = EXFAT_REPAIR_ASK;
+               break;
+       case 2:
+               ef->repair = EXFAT_REPAIR_YES;
+               break;
+       default:
+               ef->repair = EXFAT_REPAIR_NO;
+               break;
+       }
+}
+
+static bool verify_vbr_checksum(const struct exfat* ef, void* sector)
+{
+       off_t sector_size = SECTOR_SIZE(*ef->sb);
+       uint32_t vbr_checksum;
+       int i;
+
+       if (exfat_pread(ef->dev, sector, sector_size, 0) < 0)
+       {
+               exfat_error("failed to read boot sector");
+               return false;
+       }
+       vbr_checksum = exfat_vbr_start_checksum(sector, sector_size);
+       for (i = 1; i < 11; i++)
+       {
+               if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
+               {
+                       exfat_error("failed to read VBR sector");
+                       return false;
+               }
+               vbr_checksum = exfat_vbr_add_checksum(sector, sector_size,
+                               vbr_checksum);
+       }
+       if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
+       {
+               exfat_error("failed to read VBR checksum sector");
+               return false;
+       }
+       for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
+               if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum)
+               {
+                       exfat_error("invalid VBR checksum 0x%x (expected 0x%x)",
+                                       le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum);
+                       if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum))
+                               return false;
+               }
+       return true;
+}
+
+static int commit_super_block(const struct exfat* ef)
+{
+       if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+       {
+               exfat_error("failed to write super block");
+               return 1;
+       }
+       return exfat_fsync(ef->dev);
+}
+
+static int prepare_super_block(const struct exfat* ef)
+{
+       if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED)
+               exfat_warn("volume was not unmounted cleanly");
+
+       if (ef->ro)
+               return 0;
+
+       ef->sb->volume_state = cpu_to_le16(
+                       le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED);
+       return commit_super_block(ef);
+}
+
+static void exfat_free(struct exfat* ef)
+{
+       exfat_close(ef->dev);   /* first of all, close the descriptor */
+       ef->dev = NULL;                 /* struct exfat_dev is freed by exfat_close() */
+       free(ef->root);
+       ef->root = NULL;
+       free(ef->zero_cluster);
+       ef->zero_cluster = NULL;
+       free(ef->cmap.chunk);
+       ef->cmap.chunk = NULL;
+       free(ef->upcase);
+       ef->upcase = NULL;
+       free(ef->sb);
+       ef->sb = NULL;
+}
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options)
+{
+       int rc;
+       enum exfat_mode mode;
+
+       exfat_tzset();
+       memset(ef, 0, sizeof(struct exfat));
+
+       parse_options(ef, options);
+
+       if (match_option(options, "ro"))
+               mode = EXFAT_MODE_RO;
+       else if (match_option(options, "ro_fallback"))
+               mode = EXFAT_MODE_ANY;
+       else
+               mode = EXFAT_MODE_RW;
+       ef->dev = exfat_open(spec, mode);
+       if (ef->dev == NULL)
+               return -EIO;
+       if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO)
+       {
+               if (mode == EXFAT_MODE_ANY)
+                       ef->ro = -1;
+               else
+                       ef->ro = 1;
+       }
+
+       ef->sb = malloc(sizeof(struct exfat_super_block));
+       if (ef->sb == NULL)
+       {
+               exfat_error("failed to allocate memory for the super block");
+               exfat_free(ef);
+               return -ENOMEM;
+       }
+       memset(ef->sb, 0, sizeof(struct exfat_super_block));
+
+       if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+       {
+               exfat_error("failed to read boot sector");
+               exfat_free(ef);
+               return -EIO;
+       }
+       if (memcmp(ef->sb->oem_name, "EXFAT   ", 8) != 0)
+       {
+               exfat_error("exFAT file system is not found");
+               exfat_free(ef);
+               return -EIO;
+       }
+       /* sector cannot be smaller than 512 bytes */
+       if (ef->sb->sector_bits < 9)
+       {
+               exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits);
+               exfat_free(ef);
+               return -EIO;
+       }
+       /* officially exFAT supports cluster size up to 32 MB */
+       if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25)
+       {
+               exfat_error("too big cluster size: 2^(%hhd+%hhd)",
+                               ef->sb->sector_bits, ef->sb->spc_bits);
+               exfat_free(ef);
+               return -EIO;
+       }
+       ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb));
+       if (ef->zero_cluster == NULL)
+       {
+               exfat_error("failed to allocate zero sector");
+               exfat_free(ef);
+               return -ENOMEM;
+       }
+       /* use zero_cluster as a temporary buffer for VBR checksum verification */
+       if (!verify_vbr_checksum(ef, ef->zero_cluster))
+       {
+               exfat_free(ef);
+               return -EIO;
+       }
+       memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb));
+       if (ef->sb->version.major != 1 || ef->sb->version.minor != 0)
+       {
+               exfat_error("unsupported exFAT version: %hhu.%hhu",
+                               ef->sb->version.major, ef->sb->version.minor);
+               exfat_free(ef);
+               return -EIO;
+       }
+       if (ef->sb->fat_count != 1)
+       {
+               exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count);
+               exfat_free(ef);
+               return -EIO;
+       }
+       if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) >
+                       exfat_get_size(ef->dev))
+       {
+               /* this can cause I/O errors later but we don't fail mounting to let
+                  user rescue data */
+               exfat_warn("file system in sectors is larger than device: "
+                               "%"PRIu64" * %d > %"PRIu64,
+                               le64_to_cpu(ef->sb->sector_count), SECTOR_SIZE(*ef->sb),
+                               exfat_get_size(ef->dev));
+       }
+       if ((off_t) le32_to_cpu(ef->sb->cluster_count) * CLUSTER_SIZE(*ef->sb) >
+                       exfat_get_size(ef->dev))
+       {
+               exfat_error("file system in clusters is larger than device: "
+                               "%u * %d > %"PRIu64,
+                               le32_to_cpu(ef->sb->cluster_count), CLUSTER_SIZE(*ef->sb),
+                               exfat_get_size(ef->dev));
+               exfat_free(ef);
+               return -EIO;
+       }
+
+       ef->root = malloc(sizeof(struct exfat_node));
+       if (ef->root == NULL)
+       {
+               exfat_error("failed to allocate root node");
+               exfat_free(ef);
+               return -ENOMEM;
+       }
+       memset(ef->root, 0, sizeof(struct exfat_node));
+       ef->root->attrib = EXFAT_ATTRIB_DIR;
+       ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+       ef->root->fptr_cluster = ef->root->start_cluster;
+       ef->root->name[0] = cpu_to_le16('\0');
+       ef->root->size = rootdir_size(ef);
+       if (ef->root->size == 0)
+       {
+               exfat_free(ef);
+               return -EIO;
+       }
+       /* exFAT does not have time attributes for the root directory */
+       ef->root->mtime = 0;
+       ef->root->atime = 0;
+       /* always keep at least 1 reference to the root node */
+       exfat_get_node(ef->root);
+
+       rc = exfat_cache_directory(ef, ef->root);
+       if (rc != 0)
+               goto error;
+       if (ef->upcase == NULL)
+       {
+               exfat_error("upcase table is not found");
+               goto error;
+       }
+       if (ef->cmap.chunk == NULL)
+       {
+               exfat_error("clusters bitmap is not found");
+               goto error;
+       }
+
+       if (prepare_super_block(ef) != 0)
+               goto error;
+
+       return 0;
+
+error:
+       exfat_put_node(ef, ef->root);
+       exfat_reset_cache(ef);
+       exfat_free(ef);
+       return -EIO;
+}
+
+static void finalize_super_block(struct exfat* ef)
+{
+       if (ef->ro)
+               return;
+
+       ef->sb->volume_state = cpu_to_le16(
+                       le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED);
+
+       /* Some implementations set the percentage of allocated space to 0xff
+          on FS creation and never update it. In this case leave it as is. */
+       if (ef->sb->allocated_percent != 0xff)
+       {
+               uint32_t free, total;
+
+               free = exfat_count_free_clusters(ef);
+               total = le32_to_cpu(ef->sb->cluster_count);
+               ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total;
+       }
+
+       commit_super_block(ef); /* ignore return code */
+}
+
+void exfat_unmount(struct exfat* ef)
+{
+       exfat_flush_nodes(ef);  /* ignore return code */
+       exfat_flush(ef);                /* ignore return code */
+       exfat_put_node(ef, ef->root);
+       exfat_reset_cache(ef);
+       finalize_super_block(ef);
+       exfat_free(ef);                 /* will close the descriptor */
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c
new file mode 100644 (file)
index 0000000..ab1d7d6
--- /dev/null
@@ -0,0 +1,1226 @@
+/*
+       node.c (09.10.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define EXFAT_ENTRY_NONE (-1)
+
+struct exfat_node* exfat_get_node(struct exfat_node* node)
+{
+       /* if we switch to multi-threaded mode we will need atomic
+          increment here and atomic decrement in exfat_put_node() */
+       node->references++;
+       return node;
+}
+
+void exfat_put_node(struct exfat* ef, struct exfat_node* node)
+{
+       char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+
+       --node->references;
+       if (node->references < 0)
+       {
+               exfat_get_name(node, buffer);
+               exfat_bug("reference counter of '%s' is below zero", buffer);
+       }
+       else if (node->references == 0 && node != ef->root)
+       {
+               if (node->is_dirty)
+               {
+                       exfat_get_name(node, buffer);
+                       exfat_warn("dirty node '%s' with zero references", buffer);
+               }
+       }
+}
+
+/**
+ * This function must be called on rmdir and unlink (after the last
+ * exfat_put_node()) to free clusters.
+ */
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node)
+{
+       int rc = 0;
+
+       if (node->references != 0)
+               exfat_bug("unable to cleanup a node with %d references",
+                               node->references);
+
+       if (node->is_unlinked)
+       {
+               /* free all clusters and node structure itself */
+               rc = exfat_truncate(ef, node, 0, true);
+               /* free the node even in case of error or its memory will be lost */
+               free(node);
+       }
+       return rc;
+}
+
+static int read_entries(struct exfat* ef, struct exfat_node* dir,
+               struct exfat_entry* entries, int n, off_t offset)
+{
+       ssize_t size;
+
+       if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+               exfat_bug("attempted to read entries from a file");
+
+       size = exfat_generic_pread(ef, dir, entries,
+                       sizeof(struct exfat_entry[n]), offset);
+       if (size == sizeof(struct exfat_entry[n]))
+               return 0; /* success */
+       if (size == 0)
+               return -ENOENT;
+       if (size < 0)
+               return -EIO;
+       exfat_error("read %zd bytes instead of %zu bytes", size,
+                       sizeof(struct exfat_entry[n]));
+       return -EIO;
+}
+
+static int write_entries(struct exfat* ef, struct exfat_node* dir,
+               const struct exfat_entry* entries, int n, off_t offset)
+{
+       ssize_t size;
+
+       if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+               exfat_bug("attempted to write entries into a file");
+
+       size = exfat_generic_pwrite(ef, dir, entries,
+                       sizeof(struct exfat_entry[n]), offset);
+       if (size == sizeof(struct exfat_entry[n]))
+               return 0; /* success */
+       if (size < 0)
+               return -EIO;
+       exfat_error("wrote %zd bytes instead of %zu bytes", size,
+                       sizeof(struct exfat_entry[n]));
+       return -EIO;
+}
+
+static struct exfat_node* allocate_node(void)
+{
+       struct exfat_node* node = malloc(sizeof(struct exfat_node));
+       if (node == NULL)
+       {
+               exfat_error("failed to allocate node");
+               return NULL;
+       }
+       memset(node, 0, sizeof(struct exfat_node));
+       return node;
+}
+
+static void init_node_meta1(struct exfat_node* node,
+               const struct exfat_entry_meta1* meta1)
+{
+       node->attrib = le16_to_cpu(meta1->attrib);
+       node->continuations = meta1->continuations;
+       node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime,
+                       meta1->mtime_cs);
+       /* there is no centiseconds field for atime */
+       node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0);
+}
+
+static void init_node_meta2(struct exfat_node* node,
+               const struct exfat_entry_meta2* meta2)
+{
+       node->size = le64_to_cpu(meta2->size);
+       node->start_cluster = le32_to_cpu(meta2->start_cluster);
+       node->fptr_cluster = node->start_cluster;
+       node->is_contiguous = ((meta2->flags & EXFAT_FLAG_CONTIGUOUS) != 0);
+}
+
+static void init_node_name(struct exfat_node* node,
+               const struct exfat_entry* entries, int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++)
+               memcpy(node->name + i * EXFAT_ENAME_MAX,
+                               ((const struct exfat_entry_name*) &entries[i])->name,
+                               EXFAT_ENAME_MAX * sizeof(le16_t));
+}
+
+static bool check_entries(const struct exfat_entry* entry, int n)
+{
+       int previous = EXFAT_ENTRY_NONE;
+       int current;
+       int i;
+
+       /* check transitions between entries types */
+       for (i = 0; i < n + 1; previous = current, i++)
+       {
+               bool valid = false;
+
+               current = (i < n) ? entry[i].type : EXFAT_ENTRY_NONE;
+               switch (previous)
+               {
+               case EXFAT_ENTRY_NONE:
+                       valid = (current == EXFAT_ENTRY_FILE);
+                       break;
+               case EXFAT_ENTRY_FILE:
+                       valid = (current == EXFAT_ENTRY_FILE_INFO);
+                       break;
+               case EXFAT_ENTRY_FILE_INFO:
+                       valid = (current == EXFAT_ENTRY_FILE_NAME);
+                       break;
+               case EXFAT_ENTRY_FILE_NAME:
+                       valid = (current == EXFAT_ENTRY_FILE_NAME ||
+                                current == EXFAT_ENTRY_NONE ||
+                                current >= EXFAT_ENTRY_FILE_TAIL);
+                       break;
+               case EXFAT_ENTRY_FILE_TAIL ... 0xff:
+                       valid = (current >= EXFAT_ENTRY_FILE_TAIL ||
+                                current == EXFAT_ENTRY_NONE);
+                       break;
+               }
+
+               if (!valid)
+               {
+                       exfat_error("unexpected entry type %#x after %#x at %d/%d",
+                                       current, previous, i, n);
+                       return false;
+               }
+       }
+       return true;
+}
+
+static bool check_node(const struct exfat* ef, struct exfat_node* node,
+               le16_t actual_checksum, const struct exfat_entry_meta1* meta1,
+               const struct exfat_entry_meta2* meta2)
+{
+       int cluster_size = CLUSTER_SIZE(*ef->sb);
+       uint64_t clusters_heap_size =
+                       (uint64_t) le32_to_cpu(ef->sb->cluster_count) * cluster_size;
+       char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+       bool ret = true;
+
+       /*
+          Validate checksum first. If it's invalid all other fields probably
+          contain just garbage.
+       */
+       if (le16_to_cpu(actual_checksum) != le16_to_cpu(meta1->checksum))
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer,
+                               le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum));
+               if (!EXFAT_REPAIR(invalid_node_checksum, ef, node))
+                       ret = false;
+       }
+
+       /*
+          exFAT does not support sparse files but allows files with uninitialized
+          clusters. For such files valid_size means initialized data size and
+          cannot be greater than file size. See SetFileValidData() function
+          description in MSDN.
+       */
+       if (le64_to_cpu(meta2->valid_size) > node->size)
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' has valid size (%"PRIu64") greater than size "
+                               "(%"PRIu64")", buffer, le64_to_cpu(meta2->valid_size),
+                               node->size);
+               ret = false;
+       }
+
+       /*
+          Empty file must have zero start cluster. Non-empty file must start
+          with a valid cluster. Directories cannot be empty (i.e. must always
+          have a valid start cluster), but we will check this later while
+          reading that directory to give user a chance to read this directory.
+       */
+       if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE)
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' is empty but start cluster is %#x", buffer,
+                               node->start_cluster);
+               ret = false;
+       }
+       if (node->size > 0 && CLUSTER_INVALID(*ef->sb, node->start_cluster))
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' points to invalid cluster %#x", buffer,
+                               node->start_cluster);
+               ret = false;
+       }
+
+       /* File or directory cannot be larger than clusters heap. */
+       if (node->size > clusters_heap_size)
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' is larger than clusters heap: %"PRIu64" > %"PRIu64,
+                               buffer, node->size, clusters_heap_size);
+               ret = false;
+       }
+
+       /* Empty file or directory must be marked as non-contiguous. */
+       if (node->size == 0 && node->is_contiguous)
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' is empty but marked as contiguous (%#hx)", buffer,
+                               node->attrib);
+               ret = false;
+       }
+
+       /* Directory size must be aligned on at cluster boundary. */
+       if ((node->attrib & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0)
+       {
+               exfat_get_name(node, buffer);
+               exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer,
+                               node->size, cluster_size);
+               ret = false;
+       }
+
+       return ret;
+}
+
+static int parse_file_entries(struct exfat* ef, struct exfat_node* node,
+               const struct exfat_entry* entries, int n)
+{
+       const struct exfat_entry_meta1* meta1;
+       const struct exfat_entry_meta2* meta2;
+       int mandatory_entries;
+
+       if (!check_entries(entries, n))
+               return -EIO;
+
+       meta1 = (const struct exfat_entry_meta1*) &entries[0];
+       if (meta1->continuations < 2)
+       {
+               exfat_error("too few continuations (%hhu)", meta1->continuations);
+               return -EIO;
+       }
+       meta2 = (const struct exfat_entry_meta2*) &entries[1];
+       if (meta2->flags & ~(EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS))
+       {
+               exfat_error("unknown flags in meta2 (%#hhx)", meta2->flags);
+               return -EIO;
+       }
+       mandatory_entries = 2 + DIV_ROUND_UP(meta2->name_length, EXFAT_ENAME_MAX);
+       if (meta1->continuations < mandatory_entries - 1)
+       {
+               exfat_error("too few continuations (%hhu < %d)",
+                               meta1->continuations, mandatory_entries - 1);
+               return -EIO;
+       }
+
+       init_node_meta1(node, meta1);
+       init_node_meta2(node, meta2);
+       init_node_name(node, entries + 2, mandatory_entries - 2);
+
+       if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2))
+               return -EIO;
+
+       return 0;
+}
+
+static int parse_file_entry(struct exfat* ef, struct exfat_node* parent,
+               struct exfat_node** node, off_t* offset, int n)
+{
+       struct exfat_entry entries[n];
+       int rc;
+
+       rc = read_entries(ef, parent, entries, n, *offset);
+       if (rc != 0)
+               return rc;
+
+       /* a new node has zero references */
+       *node = allocate_node();
+       if (*node == NULL)
+               return -ENOMEM;
+       (*node)->entry_offset = *offset;
+
+       rc = parse_file_entries(ef, *node, entries, n);
+       if (rc != 0)
+       {
+               free(*node);
+               return rc;
+       }
+
+       *offset += sizeof(struct exfat_entry[n]);
+       return 0;
+}
+
+static void decompress_upcase(uint16_t* output, const le16_t* source,
+               size_t size)
+{
+       size_t si;
+       size_t oi;
+
+       for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++)
+               output[oi] = oi;
+
+       for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++)
+       {
+               uint16_t ch = le16_to_cpu(source[si]);
+
+               if (ch == 0xffff && si + 1 < size)      /* indicates a run */
+                       oi += le16_to_cpu(source[++si]);
+               else
+                       output[oi++] = ch;
+       }
+}
+
+/*
+ * Read one entry in a directory at offset position and build a new node
+ * structure.
+ */
+static int readdir(struct exfat* ef, struct exfat_node* parent,
+               struct exfat_node** node, off_t* offset)
+{
+       int rc;
+       struct exfat_entry entry;
+       const struct exfat_entry_meta1* meta1;
+       const struct exfat_entry_upcase* upcase;
+       const struct exfat_entry_bitmap* bitmap;
+       const struct exfat_entry_label* label;
+       uint64_t upcase_size = 0;
+       le16_t* upcase_comp = NULL;
+
+       for (;;)
+       {
+               rc = read_entries(ef, parent, &entry, 1, *offset);
+               if (rc != 0)
+                       return rc;
+
+               switch (entry.type)
+               {
+               case EXFAT_ENTRY_FILE:
+                       meta1 = (const struct exfat_entry_meta1*) &entry;
+                       return parse_file_entry(ef, parent, node, offset,
+                                       1 + meta1->continuations);
+
+               case EXFAT_ENTRY_UPCASE:
+                       if (ef->upcase != NULL)
+                               break;
+                       upcase = (const struct exfat_entry_upcase*) &entry;
+                       if (CLUSTER_INVALID(*ef->sb, le32_to_cpu(upcase->start_cluster)))
+                       {
+                               exfat_error("invalid cluster 0x%x in upcase table",
+                                               le32_to_cpu(upcase->start_cluster));
+                               return -EIO;
+                       }
+                       upcase_size = le64_to_cpu(upcase->size);
+                       if (upcase_size == 0 ||
+                               upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) ||
+                               upcase_size % sizeof(uint16_t) != 0)
+                       {
+                               exfat_error("bad upcase table size (%"PRIu64" bytes)",
+                                               upcase_size);
+                               return -EIO;
+                       }
+                       upcase_comp = malloc(upcase_size);
+                       if (upcase_comp == NULL)
+                       {
+                               exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
+                                               upcase_size);
+                               return -ENOMEM;
+                       }
+
+                       /* read compressed upcase table */
+                       if (exfat_pread(ef->dev, upcase_comp, upcase_size,
+                                       exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0)
+                       {
+                               free(upcase_comp);
+                               exfat_error("failed to read upper case table "
+                                               "(%"PRIu64" bytes starting at cluster %#x)",
+                                               upcase_size,
+                                               le32_to_cpu(upcase->start_cluster));
+                               return -EIO;
+                       }
+
+                       /* decompress upcase table */
+                       ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t));
+                       if (ef->upcase == NULL)
+                       {
+                               free(upcase_comp);
+                               exfat_error("failed to allocate decompressed upcase table");
+                               return -ENOMEM;
+                       }
+                       decompress_upcase(ef->upcase, upcase_comp,
+                                       upcase_size / sizeof(uint16_t));
+                       free(upcase_comp);
+                       break;
+
+               case EXFAT_ENTRY_BITMAP:
+                       bitmap = (const struct exfat_entry_bitmap*) &entry;
+                       ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster);
+                       if (CLUSTER_INVALID(*ef->sb, ef->cmap.start_cluster))
+                       {
+                               exfat_error("invalid cluster 0x%x in clusters bitmap",
+                                               ef->cmap.start_cluster);
+                               return -EIO;
+                       }
+                       ef->cmap.size = le32_to_cpu(ef->sb->cluster_count);
+                       if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8))
+                       {
+                               exfat_error("invalid clusters bitmap size: %"PRIu64
+                                               " (expected at least %u)",
+                                               le64_to_cpu(bitmap->size),
+                                               DIV_ROUND_UP(ef->cmap.size, 8));
+                               return -EIO;
+                       }
+                       /* FIXME bitmap can be rather big, up to 512 MB */
+                       ef->cmap.chunk_size = ef->cmap.size;
+                       ef->cmap.chunk = malloc(BMAP_SIZE(ef->cmap.chunk_size));
+                       if (ef->cmap.chunk == NULL)
+                       {
+                               exfat_error("failed to allocate clusters bitmap chunk "
+                                               "(%"PRIu64" bytes)", le64_to_cpu(bitmap->size));
+                               return -ENOMEM;
+                       }
+
+                       if (exfat_pread(ef->dev, ef->cmap.chunk,
+                                       BMAP_SIZE(ef->cmap.chunk_size),
+                                       exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+                       {
+                               exfat_error("failed to read clusters bitmap "
+                                               "(%"PRIu64" bytes starting at cluster %#x)",
+                                               le64_to_cpu(bitmap->size), ef->cmap.start_cluster);
+                               return -EIO;
+                       }
+                       break;
+
+               case EXFAT_ENTRY_LABEL:
+                       label = (const struct exfat_entry_label*) &entry;
+                       if (label->length > EXFAT_ENAME_MAX)
+                       {
+                               exfat_error("too long label (%hhu chars)", label->length);
+                               return -EIO;
+                       }
+                       if (utf16_to_utf8(ef->label, label->name,
+                                               sizeof(ef->label), EXFAT_ENAME_MAX) != 0)
+                               return -EIO;
+                       break;
+
+               default:
+                       if (!(entry.type & EXFAT_ENTRY_VALID))
+                               break; /* deleted entry, ignore it */
+
+                       exfat_error("unknown entry type %#hhx", entry.type);
+                       if (!EXFAT_REPAIR(unknown_entry, ef, parent, &entry, *offset))
+                               return -EIO;
+               }
+               *offset += sizeof(entry);
+       }
+       /* we never reach here */
+}
+
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir)
+{
+       off_t offset = 0;
+       int rc;
+       struct exfat_node* node;
+       struct exfat_node* current = NULL;
+
+       if (dir->is_cached)
+               return 0; /* already cached */
+
+       while ((rc = readdir(ef, dir, &node, &offset)) == 0)
+       {
+               node->parent = dir;
+               if (current != NULL)
+               {
+                       current->next = node;
+                       node->prev = current;
+               }
+               else
+                       dir->child = node;
+
+               current = node;
+       }
+
+       if (rc != -ENOENT)
+       {
+               /* rollback */
+               for (current = dir->child; current; current = node)
+               {
+                       node = current->next;
+                       free(current);
+               }
+               dir->child = NULL;
+               return rc;
+       }
+
+       dir->is_cached = true;
+       return 0;
+}
+
+static void tree_attach(struct exfat_node* dir, struct exfat_node* node)
+{
+       node->parent = dir;
+       if (dir->child)
+       {
+               dir->child->prev = node;
+               node->next = dir->child;
+       }
+       dir->child = node;
+}
+
+static void tree_detach(struct exfat_node* node)
+{
+       if (node->prev)
+               node->prev->next = node->next;
+       else /* this is the first node in the list */
+               node->parent->child = node->next;
+       if (node->next)
+               node->next->prev = node->prev;
+       node->parent = NULL;
+       node->prev = NULL;
+       node->next = NULL;
+}
+
+static void reset_cache(struct exfat* ef, struct exfat_node* node)
+{
+       char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+
+       while (node->child)
+       {
+               struct exfat_node* p = node->child;
+               reset_cache(ef, p);
+               tree_detach(p);
+               free(p);
+       }
+       node->is_cached = false;
+       if (node->references != 0)
+       {
+               exfat_get_name(node, buffer);
+               exfat_warn("non-zero reference counter (%d) for '%s'",
+                               node->references, buffer);
+       }
+       if (node != ef->root && node->is_dirty)
+       {
+               exfat_get_name(node, buffer);
+               exfat_bug("node '%s' is dirty", buffer);
+       }
+       while (node->references)
+               exfat_put_node(ef, node);
+}
+
+void exfat_reset_cache(struct exfat* ef)
+{
+       reset_cache(ef, ef->root);
+}
+
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
+{
+       struct exfat_entry entries[1 + node->continuations];
+       struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+       struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+       int rc;
+
+       if (!node->is_dirty)
+               return 0; /* no need to flush */
+
+       if (ef->ro)
+               exfat_bug("unable to flush node to read-only FS");
+
+       if (node->parent == NULL)
+               return 0; /* do not flush unlinked node */
+
+       rc = read_entries(ef, node->parent, entries, 1 + node->continuations,
+                       node->entry_offset);
+       if (rc != 0)
+               return rc;
+       if (!check_entries(entries, 1 + node->continuations))
+               return -EIO;
+
+       meta1->attrib = cpu_to_le16(node->attrib);
+       exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime,
+                       &meta1->mtime_cs);
+       exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL);
+       meta2->size = meta2->valid_size = cpu_to_le64(node->size);
+       meta2->start_cluster = cpu_to_le32(node->start_cluster);
+       meta2->flags = EXFAT_FLAG_ALWAYS1;
+       /* empty files must not be marked as contiguous */
+       if (node->size != 0 && node->is_contiguous)
+               meta2->flags |= EXFAT_FLAG_CONTIGUOUS;
+       /* name hash remains unchanged, no need to recalculate it */
+
+       meta1->checksum = exfat_calc_checksum(entries, 1 + node->continuations);
+       rc = write_entries(ef, node->parent, entries, 1 + node->continuations,
+                       node->entry_offset);
+       if (rc != 0)
+               return rc;
+
+       node->is_dirty = false;
+       return exfat_flush(ef);
+}
+
+static int erase_entries(struct exfat* ef, struct exfat_node* dir, int n,
+               off_t offset)
+{
+       struct exfat_entry entries[n];
+       int rc;
+       int i;
+
+       rc = read_entries(ef, dir, entries, n, offset);
+       if (rc != 0)
+               return rc;
+       for (i = 0; i < n; i++)
+               entries[i].type &= ~EXFAT_ENTRY_VALID;
+       return write_entries(ef, dir, entries, n, offset);
+}
+
+static int erase_node(struct exfat* ef, struct exfat_node* node)
+{
+       int rc;
+
+       exfat_get_node(node->parent);
+       rc = erase_entries(ef, node->parent, 1 + node->continuations,
+                       node->entry_offset);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, node->parent);
+               return rc;
+       }
+       rc = exfat_flush_node(ef, node->parent);
+       exfat_put_node(ef, node->parent);
+       return rc;
+}
+
+static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
+               off_t deleted_offset)
+{
+       const struct exfat_node* node;
+       const struct exfat_node* last_node;
+       uint64_t entries = 0;
+       uint64_t new_size;
+
+       if (!(dir->attrib & EXFAT_ATTRIB_DIR))
+               exfat_bug("attempted to shrink a file");
+       if (!dir->is_cached)
+               exfat_bug("attempted to shrink uncached directory");
+
+       for (last_node = node = dir->child; node; node = node->next)
+       {
+               if (deleted_offset < node->entry_offset)
+               {
+                       /* there are other entries after the removed one, no way to shrink
+                          this directory */
+                       return 0;
+               }
+               if (last_node->entry_offset < node->entry_offset)
+                       last_node = node;
+       }
+
+       if (last_node)
+       {
+               /* offset of the last entry */
+               entries += last_node->entry_offset / sizeof(struct exfat_entry);
+               /* two subentries with meta info */
+               entries += 2;
+               /* subentries with file name */
+               entries += DIV_ROUND_UP(utf16_length(last_node->name),
+                               EXFAT_ENAME_MAX);
+       }
+
+       new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry),
+                                CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb);
+       if (new_size == 0) /* directory always has at least 1 cluster */
+               new_size = CLUSTER_SIZE(*ef->sb);
+       if (new_size == dir->size)
+               return 0;
+       return exfat_truncate(ef, dir, new_size, true);
+}
+
+static int delete(struct exfat* ef, struct exfat_node* node)
+{
+       struct exfat_node* parent = node->parent;
+       off_t deleted_offset = node->entry_offset;
+       int rc;
+
+       exfat_get_node(parent);
+       rc = erase_node(ef, node);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, parent);
+               return rc;
+       }
+       tree_detach(node);
+       rc = shrink_directory(ef, parent, deleted_offset);
+       node->is_unlinked = true;
+       if (rc != 0)
+       {
+               exfat_flush_node(ef, parent);
+               exfat_put_node(ef, parent);
+               return rc;
+       }
+       exfat_update_mtime(parent);
+       rc = exfat_flush_node(ef, parent);
+       exfat_put_node(ef, parent);
+       return rc;
+}
+
+int exfat_unlink(struct exfat* ef, struct exfat_node* node)
+{
+       if (node->attrib & EXFAT_ATTRIB_DIR)
+               return -EISDIR;
+       return delete(ef, node);
+}
+
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node)
+{
+       int rc;
+
+       if (!(node->attrib & EXFAT_ATTRIB_DIR))
+               return -ENOTDIR;
+       /* check that directory is empty */
+       rc = exfat_cache_directory(ef, node);
+       if (rc != 0)
+               return rc;
+       if (node->child)
+               return -ENOTEMPTY;
+       return delete(ef, node);
+}
+
+static int check_slot(struct exfat* ef, struct exfat_node* dir, off_t offset,
+               int n)
+{
+       struct exfat_entry entries[n];
+       int rc;
+       size_t i;
+
+       /* Root directory contains entries, that don't have any nodes associated
+          with them (clusters bitmap, upper case table, label). We need to be
+          careful not to overwrite them. */
+       if (dir != ef->root)
+               return 0;
+
+       rc = read_entries(ef, dir, entries, n, offset);
+       if (rc != 0)
+               return rc;
+       for (i = 0; i < n; i++)
+               if (entries[i].type & EXFAT_ENTRY_VALID)
+                       return -EINVAL;
+       return 0;
+}
+
+static int find_slot(struct exfat* ef, struct exfat_node* dir,
+               off_t* offset, int n)
+{
+       bitmap_t* dmap;
+       struct exfat_node* p;
+       size_t i;
+       int contiguous = 0;
+
+       if (!dir->is_cached)
+               exfat_bug("directory is not cached");
+
+       /* build a bitmap of valid entries in the directory */
+       dmap = calloc(BMAP_SIZE(dir->size / sizeof(struct exfat_entry)),
+                       sizeof(bitmap_t));
+       if (dmap == NULL)
+       {
+               exfat_error("failed to allocate directory bitmap (%"PRIu64")",
+                               dir->size / sizeof(struct exfat_entry));
+               return -ENOMEM;
+       }
+       for (p = dir->child; p != NULL; p = p->next)
+               for (i = 0; i < 1 + p->continuations; i++)
+                       BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i);
+
+       /* find a slot in the directory entries bitmap */
+       for (i = 0; i < dir->size / sizeof(struct exfat_entry); i++)
+       {
+               if (BMAP_GET(dmap, i) == 0)
+               {
+                       if (contiguous++ == 0)
+                               *offset = (off_t) i * sizeof(struct exfat_entry);
+                       if (contiguous == n)
+                               /* suitable slot is found, check that it's not occupied */
+                               switch (check_slot(ef, dir, *offset, n))
+                               {
+                               case 0:
+                                       free(dmap);
+                                       return 0;
+                               case -EIO:
+                                       free(dmap);
+                                       return -EIO;
+                               case -EINVAL:
+                                       /* slot at (i-n) is occupied, go back and check (i-n+1) */
+                                       i -= contiguous - 1;
+                                       contiguous = 0;
+                                       break;
+                               }
+               }
+               else
+                       contiguous = 0;
+       }
+       free(dmap);
+
+       /* no suitable slots found, extend the directory */
+       if (contiguous == 0)
+               *offset = dir->size;
+       return exfat_truncate(ef, dir,
+                       ROUND_UP(dir->size + sizeof(struct exfat_entry[n - contiguous]),
+                                       CLUSTER_SIZE(*ef->sb)),
+                       true);
+}
+
+static int commit_entry(struct exfat* ef, struct exfat_node* dir,
+               const le16_t* name, off_t offset, uint16_t attrib)
+{
+       struct exfat_node* node;
+       const size_t name_length = utf16_length(name);
+       const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+       struct exfat_entry entries[2 + name_entries];
+       struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+       struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+       int i;
+       int rc;
+
+       memset(entries, 0, sizeof(struct exfat_entry[2]));
+
+       meta1->type = EXFAT_ENTRY_FILE;
+       meta1->continuations = 1 + name_entries;
+       meta1->attrib = cpu_to_le16(attrib);
+       exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime,
+                       &meta1->crtime_cs);
+       meta1->adate = meta1->mdate = meta1->crdate;
+       meta1->atime = meta1->mtime = meta1->crtime;
+       meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */
+
+       meta2->type = EXFAT_ENTRY_FILE_INFO;
+       meta2->flags = EXFAT_FLAG_ALWAYS1;
+       meta2->name_length = name_length;
+       meta2->name_hash = exfat_calc_name_hash(ef, name, name_length);
+       meta2->start_cluster = cpu_to_le32(EXFAT_CLUSTER_FREE);
+
+       for (i = 0; i < name_entries; i++)
+       {
+               struct exfat_entry_name* name_entry;
+
+               name_entry = (struct exfat_entry_name*) &entries[2 + i];
+               name_entry->type = EXFAT_ENTRY_FILE_NAME;
+               name_entry->__unknown = 0;
+               memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX,
+                               EXFAT_ENAME_MAX * sizeof(le16_t));
+       }
+
+       meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries);
+       rc = write_entries(ef, dir, entries, 2 + name_entries, offset);
+       if (rc != 0)
+               return rc;
+
+       node = allocate_node();
+       if (node == NULL)
+               return -ENOMEM;
+       node->entry_offset = offset;
+       memcpy(node->name, name, name_length * sizeof(le16_t));
+       init_node_meta1(node, meta1);
+       init_node_meta2(node, meta2);
+
+       tree_attach(dir, node);
+       return 0;
+}
+
+static int create(struct exfat* ef, const char* path, uint16_t attrib)
+{
+       struct exfat_node* dir;
+       struct exfat_node* existing;
+       off_t offset = -1;
+       le16_t name[EXFAT_NAME_MAX + 1];
+       int rc;
+
+       rc = exfat_split(ef, &dir, &existing, name, path);
+       if (rc != 0)
+               return rc;
+       if (existing != NULL)
+       {
+               exfat_put_node(ef, existing);
+               exfat_put_node(ef, dir);
+               return -EEXIST;
+       }
+
+       rc = find_slot(ef, dir, &offset,
+                       2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+       if (rc != 0)
+       {
+               exfat_put_node(ef, dir);
+               return rc;
+       }
+       rc = commit_entry(ef, dir, name, offset, attrib);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, dir);
+               return rc;
+       }
+       exfat_update_mtime(dir);
+       rc = exfat_flush_node(ef, dir);
+       exfat_put_node(ef, dir);
+       return rc;
+}
+
+int exfat_mknod(struct exfat* ef, const char* path)
+{
+       return create(ef, path, EXFAT_ATTRIB_ARCH);
+}
+
+int exfat_mkdir(struct exfat* ef, const char* path)
+{
+       int rc;
+       struct exfat_node* node;
+
+       rc = create(ef, path, EXFAT_ATTRIB_DIR);
+       if (rc != 0)
+               return rc;
+       rc = exfat_lookup(ef, &node, path);
+       if (rc != 0)
+               return 0;
+       /* directories always have at least one cluster */
+       rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true);
+       if (rc != 0)
+       {
+               delete(ef, node);
+               exfat_put_node(ef, node);
+               return rc;
+       }
+       rc = exfat_flush_node(ef, node);
+       if (rc != 0)
+       {
+               delete(ef, node);
+               exfat_put_node(ef, node);
+               return rc;
+       }
+       exfat_put_node(ef, node);
+       return 0;
+}
+
+static int rename_entry(struct exfat* ef, struct exfat_node* dir,
+               struct exfat_node* node, const le16_t* name, off_t new_offset)
+{
+       const size_t name_length = utf16_length(name);
+       const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+       struct exfat_entry entries[2 + name_entries];
+       struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
+       struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
+       int rc;
+       int i;
+
+       rc = read_entries(ef, node->parent, entries, 2, node->entry_offset);
+       if (rc != 0)
+               return rc;
+
+       meta1->continuations = 1 + name_entries;
+       meta2->name_length = name_length;
+       meta2->name_hash = exfat_calc_name_hash(ef, name, name_length);
+
+       rc = erase_node(ef, node);
+       if (rc != 0)
+               return rc;
+
+       node->entry_offset = new_offset;
+       node->continuations = 1 + name_entries;
+
+       for (i = 0; i < name_entries; i++)
+       {
+               struct exfat_entry_name* name_entry;
+
+               name_entry = (struct exfat_entry_name*) &entries[2 + i];
+               name_entry->type = EXFAT_ENTRY_FILE_NAME;
+               name_entry->__unknown = 0;
+               memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX,
+                               EXFAT_ENAME_MAX * sizeof(le16_t));
+       }
+
+       meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries);
+       rc = write_entries(ef, dir, entries, 2 + name_entries, new_offset);
+       if (rc != 0)
+               return rc;
+
+       memcpy(node->name, name, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+       tree_detach(node);
+       tree_attach(dir, node);
+       return 0;
+}
+
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
+{
+       struct exfat_node* node;
+       struct exfat_node* existing;
+       struct exfat_node* dir;
+       off_t offset = -1;
+       le16_t name[EXFAT_NAME_MAX + 1];
+       int rc;
+
+       rc = exfat_lookup(ef, &node, old_path);
+       if (rc != 0)
+               return rc;
+
+       rc = exfat_split(ef, &dir, &existing, name, new_path);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, node);
+               return rc;
+       }
+
+       /* check that target is not a subdirectory of the source */
+       if (node->attrib & EXFAT_ATTRIB_DIR)
+       {
+               struct exfat_node* p;
+
+               for (p = dir; p; p = p->parent)
+                       if (node == p)
+                       {
+                               if (existing != NULL)
+                                       exfat_put_node(ef, existing);
+                               exfat_put_node(ef, dir);
+                               exfat_put_node(ef, node);
+                               return -EINVAL;
+                       }
+       }
+
+       if (existing != NULL)
+       {
+               /* remove target if it's not the same node as source */
+               if (existing != node)
+               {
+                       if (existing->attrib & EXFAT_ATTRIB_DIR)
+                       {
+                               if (node->attrib & EXFAT_ATTRIB_DIR)
+                                       rc = exfat_rmdir(ef, existing);
+                               else
+                                       rc = -ENOTDIR;
+                       }
+                       else
+                       {
+                               if (!(node->attrib & EXFAT_ATTRIB_DIR))
+                                       rc = exfat_unlink(ef, existing);
+                               else
+                                       rc = -EISDIR;
+                       }
+                       exfat_put_node(ef, existing);
+                       if (rc != 0)
+                       {
+                               /* free clusters even if something went wrong; overwise they
+                                  will be just lost */
+                               exfat_cleanup_node(ef, existing);
+                               exfat_put_node(ef, dir);
+                               exfat_put_node(ef, node);
+                               return rc;
+                       }
+                       rc = exfat_cleanup_node(ef, existing);
+                       if (rc != 0)
+                       {
+                               exfat_put_node(ef, dir);
+                               exfat_put_node(ef, node);
+                               return rc;
+                       }
+               }
+               else
+                       exfat_put_node(ef, existing);
+       }
+
+       rc = find_slot(ef, dir, &offset,
+                       2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+       if (rc != 0)
+       {
+               exfat_put_node(ef, dir);
+               exfat_put_node(ef, node);
+               return rc;
+       }
+       rc = rename_entry(ef, dir, node, name, offset);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, dir);
+               exfat_put_node(ef, node);
+               return rc;
+       }
+       rc = exfat_flush_node(ef, dir);
+       exfat_put_node(ef, dir);
+       exfat_put_node(ef, node);
+       /* node itself is not marked as dirty, no need to flush it */
+       return rc;
+}
+
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2])
+{
+       node->atime = tv[0].tv_sec;
+       node->mtime = tv[1].tv_sec;
+       node->is_dirty = true;
+}
+
+void exfat_update_atime(struct exfat_node* node)
+{
+       node->atime = time(NULL);
+       node->is_dirty = true;
+}
+
+void exfat_update_mtime(struct exfat_node* node)
+{
+       node->mtime = time(NULL);
+       node->is_dirty = true;
+}
+
+const char* exfat_get_label(struct exfat* ef)
+{
+       return ef->label;
+}
+
+static int find_label(struct exfat* ef, off_t* offset)
+{
+       struct exfat_entry entry;
+       int rc;
+
+       for (*offset = 0; ; *offset += sizeof(entry))
+       {
+               rc = read_entries(ef, ef->root, &entry, 1, *offset);
+               if (rc != 0)
+                       return rc;
+
+               if (entry.type == EXFAT_ENTRY_LABEL)
+                       return 0;
+       }
+}
+
+int exfat_set_label(struct exfat* ef, const char* label)
+{
+       le16_t label_utf16[EXFAT_ENAME_MAX + 1];
+       int rc;
+       off_t offset;
+       struct exfat_entry_label entry;
+
+       memset(label_utf16, 0, sizeof(label_utf16));
+       rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, strlen(label));
+       if (rc != 0)
+               return rc;
+
+       rc = find_label(ef, &offset);
+       if (rc == -ENOENT)
+               rc = find_slot(ef, ef->root, &offset, 1);
+       if (rc != 0)
+               return rc;
+
+       entry.type = EXFAT_ENTRY_LABEL;
+       entry.length = utf16_length(label_utf16);
+       memcpy(entry.name, label_utf16, sizeof(entry.name));
+       if (entry.length == 0)
+               entry.type ^= EXFAT_ENTRY_VALID;
+
+       rc = write_entries(ef, ef->root, (struct exfat_entry*) &entry, 1, offset);
+       if (rc != 0)
+               return rc;
+
+       strcpy(ef->label, label);
+       return 0;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h
new file mode 100644 (file)
index 0000000..9ab3155
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+       platform.h (14.05.13)
+       OS-specific code (libc-specific in fact). Note that systems with the
+       same kernel can use different libc implementations.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef PLATFORM_H_INCLUDED
+#define PLATFORM_H_INCLUDED
+
+#if defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)
+
+#include <endian.h>
+#include <byteswap.h>
+#define exfat_bswap16(x) bswap_16(x)
+#define exfat_bswap32(x) bswap_32(x)
+#define exfat_bswap64(x) bswap_64(x)
+#define EXFAT_BYTE_ORDER __BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN __LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN __BIG_ENDIAN
+
+#elif defined(__APPLE__)
+
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
+#define exfat_bswap16(x) OSSwapInt16(x)
+#define exfat_bswap32(x) OSSwapInt32(x)
+#define exfat_bswap64(x) OSSwapInt64(x)
+#define EXFAT_BYTE_ORDER BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN BIG_ENDIAN
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
+
+#include <sys/endian.h>
+#define exfat_bswap16(x) bswap16(x)
+#define exfat_bswap32(x) bswap32(x)
+#define exfat_bswap64(x) bswap64(x)
+#define EXFAT_BYTE_ORDER _BYTE_ORDER
+#define EXFAT_LITTLE_ENDIAN _LITTLE_ENDIAN
+#define EXFAT_BIG_ENDIAN _BIG_ENDIAN
+
+#else 
+#error Unknown platform
+#endif
+
+#endif /* ifndef PLATFORM_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c
new file mode 100644 (file)
index 0000000..620ad52
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+       repair.c (09.03.17)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <strings.h>
+
+int exfat_errors_fixed;
+
+bool exfat_ask_to_fix(const struct exfat* ef)
+{
+       const char* question = "Fix (Y/N)?";
+       char answer[8];
+       bool yeah, nope;
+
+       switch (ef->repair)
+       {
+       case EXFAT_REPAIR_NO:
+               return false;
+       case EXFAT_REPAIR_YES:
+               printf("%s %s", question, "Y\n");
+               return true;
+       case EXFAT_REPAIR_ASK:
+               do
+               {
+                       printf("%s ", question);
+                       fflush(stdout);
+                       if (fgets(answer, sizeof(answer), stdin))
+                       {
+                               yeah = strcasecmp(answer, "Y\n") == 0;
+                               nope = strcasecmp(answer, "N\n") == 0;
+                       }
+                       else
+                       {
+                               yeah = false;
+                               nope = true;
+                       }
+               }
+               while (!yeah && !nope);
+               return yeah;
+       }
+       exfat_bug("invalid repair option value: %d", ef->repair);
+    return false;
+}
+
+bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
+               uint32_t vbr_checksum)
+{
+       size_t i;
+       off_t sector_size = SECTOR_SIZE(*ef->sb);
+
+       for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
+               ((le32_t*) sector)[i] = cpu_to_le32(vbr_checksum);
+       if (exfat_pwrite(ef->dev, sector, sector_size, 11 * sector_size) < 0)
+       {
+               exfat_error("failed to write correct VBR checksum");
+               return false;
+       }
+       exfat_errors_fixed++;
+       return true;
+}
+
+bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
+               struct exfat_node* node)
+{
+       /* checksum will be rewritten by exfat_flush_node() */
+       node->is_dirty = true;
+
+       exfat_errors_fixed++;
+       return true;
+}
+
+bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
+               const struct exfat_entry* entry, off_t offset)
+{
+       struct exfat_entry deleted = *entry;
+
+       deleted.type &= ~EXFAT_ENTRY_VALID;
+       if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry),
+                       offset) != sizeof(struct exfat_entry))
+               return false;
+
+       exfat_errors_fixed++;
+       return true;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c
new file mode 100644 (file)
index 0000000..31ae5a2
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+       time.c (03.02.12)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+
+/* timezone offset from UTC in seconds; positive for western timezones,
+   negative for eastern ones */
+static long exfat_timezone;
+
+#define SEC_IN_MIN 60ll
+#define SEC_IN_HOUR (60 * SEC_IN_MIN)
+#define SEC_IN_DAY (24 * SEC_IN_HOUR)
+#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */
+/* Unix epoch started at 0:00:00 UTC 1 January 1970 */
+#define UNIX_EPOCH_YEAR 1970
+/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */
+#define EXFAT_EPOCH_YEAR 1980
+/* number of years from Unix epoch to exFAT epoch */
+#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR)
+/* number of days from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4)
+/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY)
+/* number of leap years passed from exFAT epoch to the specified year
+   (excluding the specified year itself) */
+#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \
+               - (EXFAT_EPOCH_YEAR - 1) / 4)
+/* checks whether the specified year is leap */
+#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0)
+
+static const time_t days_in_year[] =
+{
+       /* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
+       0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
+{
+       time_t unix_time = EPOCH_DIFF_SEC;
+       uint16_t ndate = le16_to_cpu(date);
+       uint16_t ntime = le16_to_cpu(time);
+
+       uint16_t day    = ndate & 0x1f;      /* 5 bits, 1-31 */
+       uint16_t month  = ndate >> 5 & 0xf;  /* 4 bits, 1-12 */
+       uint16_t year   = ndate >> 9;        /* 7 bits, 1-127 (+1980) */
+
+       uint16_t twosec = ntime & 0x1f;      /* 5 bits, 0-29 (2 sec granularity) */
+       uint16_t min    = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */
+       uint16_t hour   = ntime >> 11;       /* 5 bits, 0-23 */
+
+       if (day == 0 || month == 0 || month > 12)
+       {
+               exfat_error("bad date %u-%02hu-%02hu",
+                               year + EXFAT_EPOCH_YEAR, month, day);
+               return 0;
+       }
+       if (hour > 23 || min > 59 || twosec > 29)
+       {
+               exfat_error("bad time %hu:%02hu:%02u",
+                               hour, min, twosec * 2);
+               return 0;
+       }
+       if (centisec > 199)
+       {
+               exfat_error("bad centiseconds count %hhu", centisec);
+               return 0;
+       }
+
+       /* every 4th year between 1904 and 2096 is leap */
+       unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY;
+       unix_time += days_in_year[month] * SEC_IN_DAY;
+       /* if it's leap year and February has passed we should add 1 day */
+       if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2)
+               unix_time += SEC_IN_DAY;
+       unix_time += (day - 1) * SEC_IN_DAY;
+
+       unix_time += hour * SEC_IN_HOUR;
+       unix_time += min * SEC_IN_MIN;
+       /* exFAT represents time with 2 sec granularity */
+       unix_time += twosec * 2;
+       unix_time += centisec / 100;
+
+       /* exFAT stores timestamps in local time, so we correct it to UTC */
+       unix_time += exfat_timezone;
+
+       return unix_time;
+}
+
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+               uint8_t* centisec)
+{
+       time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
+       uint16_t day, month, year;
+       uint16_t twosec, min, hour;
+       int days;
+       int i;
+
+       /* time before exFAT epoch cannot be represented */
+       if (unix_time < shift)
+               unix_time = shift;
+
+       unix_time -= shift;
+
+       days = unix_time / SEC_IN_DAY;
+       year = (4 * days) / (4 * 365 + 1);
+       days -= year * 365 + LEAP_YEARS(year);
+       month = 0;
+       for (i = 1; i <= 12; i++)
+       {
+               int leap_day = (IS_LEAP_YEAR(year) && i == 2);
+               int leap_sub = (IS_LEAP_YEAR(year) && i >= 3);
+
+               if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day)
+               {
+                       month = i;
+                       days -= days_in_year[i] + leap_sub;
+                       break;
+               }
+       }
+       day = days + 1;
+
+       hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR;
+       min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN;
+       twosec = (unix_time % SEC_IN_MIN) / 2;
+
+       *date = cpu_to_le16(day | (month << 5) | (year << 9));
+       *time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
+       if (centisec)
+               *centisec = (unix_time % 2) * 100;
+}
+
+void exfat_tzset(void)
+{
+       time_t now;
+       struct tm* utc;
+
+       tzset();
+       now = time(NULL);
+       utc = gmtime(&now);
+       /* gmtime() always sets tm_isdst to 0 because daylight savings never
+          affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether
+          summer time is in effect. */
+       utc->tm_isdst = -1;
+       exfat_timezone = mktime(utc) - now;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c
new file mode 100644 (file)
index 0000000..0d0018c
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+       utf.c (13.09.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+
+static char* wchar_to_utf8(char* output, wchar_t wc, size_t outsize)
+{
+       if (wc <= 0x7f)
+       {
+               if (outsize < 1)
+                       return NULL;
+               *output++ = (char) wc;
+       }
+       else if (wc <= 0x7ff)
+       {
+               if (outsize < 2)
+                       return NULL;
+               *output++ = 0xc0 | (wc >> 6);
+               *output++ = 0x80 | (wc & 0x3f);
+       }
+       else if (wc <= 0xffff)
+       {
+               if (outsize < 3)
+                       return NULL;
+               *output++ = 0xe0 | (wc >> 12);
+               *output++ = 0x80 | ((wc >> 6) & 0x3f);
+               *output++ = 0x80 | (wc & 0x3f);
+       }
+       else if (wc <= 0x1fffff)
+       {
+               if (outsize < 4)
+                       return NULL;
+               *output++ = 0xf0 | (wc >> 18);
+               *output++ = 0x80 | ((wc >> 12) & 0x3f);
+               *output++ = 0x80 | ((wc >> 6) & 0x3f);
+               *output++ = 0x80 | (wc & 0x3f);
+       }
+       else if (wc <= 0x3ffffff)
+       {
+               if (outsize < 5)
+                       return NULL;
+               *output++ = 0xf8 | (wc >> 24);
+               *output++ = 0x80 | ((wc >> 18) & 0x3f);
+               *output++ = 0x80 | ((wc >> 12) & 0x3f);
+               *output++ = 0x80 | ((wc >> 6) & 0x3f);
+               *output++ = 0x80 | (wc & 0x3f);
+       }
+       else if (wc <= 0x7fffffff)
+       {
+               if (outsize < 6)
+                       return NULL;
+               *output++ = 0xfc | (wc >> 30);
+               *output++ = 0x80 | ((wc >> 24) & 0x3f);
+               *output++ = 0x80 | ((wc >> 18) & 0x3f);
+               *output++ = 0x80 | ((wc >> 12) & 0x3f);
+               *output++ = 0x80 | ((wc >> 6) & 0x3f);
+               *output++ = 0x80 | (wc & 0x3f);
+       }
+       else
+               return NULL;
+
+       return output;
+}
+
+static const le16_t* utf16_to_wchar(const le16_t* input, wchar_t* wc,
+               size_t insize)
+{
+       if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800)
+       {
+               if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
+                       return NULL;
+               *wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
+               *wc |= (le16_to_cpu(input[1]) & 0x3ff);
+               *wc += 0x10000;
+               return input + 2;
+       }
+       else
+       {
+               *wc = le16_to_cpu(*input);
+               return input + 1;
+       }
+}
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+               size_t insize)
+{
+       const le16_t* inp = input;
+       char* outp = output;
+       wchar_t wc;
+
+       while (inp - input < insize)
+       {
+               inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
+               if (inp == NULL)
+               {
+                       exfat_error("illegal UTF-16 sequence");
+                       return -EILSEQ;
+               }
+               outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
+               if (outp == NULL)
+               {
+                       exfat_error("name is too long");
+                       return -ENAMETOOLONG;
+               }
+               if (wc == 0)
+                       return 0;
+       }
+       if (outp - output >= outsize)
+       {
+               exfat_error("name is too long");
+               return -ENAMETOOLONG;
+       }
+       *outp = '\0';
+       return 0;
+}
+
+static const char* utf8_to_wchar(const char* input, wchar_t* wc,
+               size_t insize)
+{
+       if ((input[0] & 0x80) == 0 && insize >= 1)
+       {
+               *wc = (wchar_t) input[0];
+               return input + 1;
+       }
+       if ((input[0] & 0xe0) == 0xc0 && insize >= 2)
+       {
+               *wc = (((wchar_t) input[0] & 0x1f) << 6) |
+                      ((wchar_t) input[1] & 0x3f);
+               return input + 2;
+       }
+       if ((input[0] & 0xf0) == 0xe0 && insize >= 3)
+       {
+               *wc = (((wchar_t) input[0] & 0x0f) << 12) |
+                     (((wchar_t) input[1] & 0x3f) << 6) |
+                      ((wchar_t) input[2] & 0x3f);
+               return input + 3;
+       }
+       if ((input[0] & 0xf8) == 0xf0 && insize >= 4)
+       {
+               *wc = (((wchar_t) input[0] & 0x07) << 18) |
+                     (((wchar_t) input[1] & 0x3f) << 12) |
+                     (((wchar_t) input[2] & 0x3f) << 6) |
+                      ((wchar_t) input[3] & 0x3f);
+               return input + 4;
+       }
+       if ((input[0] & 0xfc) == 0xf8 && insize >= 5)
+       {
+               *wc = (((wchar_t) input[0] & 0x03) << 24) |
+                     (((wchar_t) input[1] & 0x3f) << 18) |
+                     (((wchar_t) input[2] & 0x3f) << 12) |
+                     (((wchar_t) input[3] & 0x3f) << 6) |
+                      ((wchar_t) input[4] & 0x3f);
+               return input + 5;
+       }
+       if ((input[0] & 0xfe) == 0xfc && insize >= 6)
+       {
+               *wc = (((wchar_t) input[0] & 0x01) << 30) |
+                     (((wchar_t) input[1] & 0x3f) << 24) |
+                     (((wchar_t) input[2] & 0x3f) << 18) |
+                     (((wchar_t) input[3] & 0x3f) << 12) |
+                     (((wchar_t) input[4] & 0x3f) << 6) |
+                      ((wchar_t) input[5] & 0x3f);
+               return input + 6;
+       }
+       return NULL;
+}
+
+static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize)
+{
+       if (wc <= 0xffff) /* if character is from BMP */
+       {
+               if (outsize == 0)
+                       return NULL;
+               output[0] = cpu_to_le16(wc);
+               return output + 1;
+       }
+       if (outsize < 2)
+               return NULL;
+       wc -= 0x10000;
+       output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
+       output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
+       return output + 2;
+}
+
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+               size_t insize)
+{
+       const char* inp = input;
+       le16_t* outp = output;
+       wchar_t wc;
+
+       while (inp - input < insize)
+       {
+               inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
+               if (inp == NULL)
+               {
+                       exfat_error("illegal UTF-8 sequence");
+                       return -EILSEQ;
+               }
+               outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
+               if (outp == NULL)
+               {
+                       exfat_error("name is too long");
+                       return -ENAMETOOLONG;
+               }
+               if (wc == 0)
+                       break;
+       }
+       if (outp - output >= outsize)
+       {
+               exfat_error("name is too long");
+               return -ENAMETOOLONG;
+       }
+       *outp = cpu_to_le16(0);
+       return 0;
+}
+
+size_t utf16_length(const le16_t* str)
+{
+       size_t i = 0;
+
+       while (le16_to_cpu(str[i]))
+               i++;
+       return i;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c
new file mode 100644 (file)
index 0000000..7baa663
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+       utils.c (04.09.09)
+       exFAT file system implementation library.
+
+       Free exFAT implementation.
+       Copyright (C) 2010-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+               struct stat* stbuf)
+{
+       memset(stbuf, 0, sizeof(struct stat));
+       if (node->attrib & EXFAT_ATTRIB_DIR)
+               stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask);
+       else
+               stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask);
+       stbuf->st_nlink = 1;
+       stbuf->st_uid = ef->uid;
+       stbuf->st_gid = ef->gid;
+       stbuf->st_size = node->size;
+       stbuf->st_blocks = ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) / 512;
+       stbuf->st_mtime = node->mtime;
+       stbuf->st_atime = node->atime;
+       /* set ctime to mtime to ensure we don't break programs that rely on ctime
+          (e.g. rsync) */
+       stbuf->st_ctime = node->mtime;
+}
+
+void exfat_get_name(const struct exfat_node* node,
+               char buffer[EXFAT_UTF8_NAME_BUFFER_MAX])
+{
+       if (utf16_to_utf8(buffer, node->name, EXFAT_UTF8_NAME_BUFFER_MAX,
+                               EXFAT_NAME_MAX) != 0)
+               exfat_bug("failed to convert name to UTF-8");
+}
+
+static uint16_t add_checksum_byte(uint16_t sum, uint8_t byte)
+{
+       return ((sum << 15) | (sum >> 1)) + byte;
+}
+
+static uint16_t add_checksum_bytes(uint16_t sum, const void* buffer, size_t n)
+{
+       int i;
+
+       for (i = 0; i < n; i++)
+               sum = add_checksum_byte(sum, ((const uint8_t*) buffer)[i]);
+       return sum;
+}
+
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry)
+{
+       uint16_t sum = 0;
+       int i;
+
+       for (i = 0; i < sizeof(struct exfat_entry); i++)
+               if (i != 2 && i != 3) /* skip checksum field itself */
+                       sum = add_checksum_byte(sum, ((const uint8_t*) entry)[i]);
+       return sum;
+}
+
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum)
+{
+       return add_checksum_bytes(sum, entry, sizeof(struct exfat_entry));
+}
+
+le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n)
+{
+       uint16_t checksum;
+       int i;
+
+       checksum = exfat_start_checksum((const struct exfat_entry_meta1*) entries);
+       for (i = 1; i < n; i++)
+               checksum = exfat_add_checksum(entries + i, checksum);
+       return cpu_to_le16(checksum);
+}
+
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size)
+{
+       size_t i;
+       uint32_t sum = 0;
+
+       for (i = 0; i < size; i++)
+               /* skip volume_state and allocated_percent fields */
+               if (i != 0x6a && i != 0x6b && i != 0x70)
+                       sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+       return sum;
+}
+
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum)
+{
+       size_t i;
+
+       for (i = 0; i < size; i++)
+               sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+       return sum;
+}
+
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
+               size_t length)
+{
+       size_t i;
+       uint16_t hash = 0;
+
+       for (i = 0; i < length; i++)
+       {
+               uint16_t c = le16_to_cpu(name[i]);
+
+               /* convert to upper case */
+               c = ef->upcase[c];
+
+               hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
+               hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+       }
+       return cpu_to_le16(hash);
+}
+
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb)
+{
+       size_t i;
+       /* 16 EB (minus 1 byte) is the largest size that can be represented by
+          uint64_t */
+       const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
+       uint64_t divisor = 1;
+       uint64_t temp = 0;
+
+       for (i = 0; ; i++, divisor *= 1024)
+       {
+               temp = (value + divisor / 2) / divisor;
+
+               if (temp == 0)
+                       break;
+               if (temp / 1024 * 1024 == temp)
+                       continue;
+               if (temp < 10240)
+                       break;
+       }
+       hb->value = temp;
+       hb->unit = units[i];
+}
+
+void exfat_print_info(const struct exfat_super_block* sb,
+               uint32_t free_clusters)
+{
+       struct exfat_human_bytes hb;
+       off_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb);
+       off_t avail_space = (off_t) free_clusters * CLUSTER_SIZE(*sb);
+
+       printf("File system version           %hhu.%hhu\n",
+                       sb->version.major, sb->version.minor);
+       exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb);
+       printf("Sector size          %10"PRIu64" %s\n", hb.value, hb.unit);
+       exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb);
+       printf("Cluster size         %10"PRIu64" %s\n", hb.value, hb.unit);
+       exfat_humanize_bytes(total_space, &hb);
+       printf("Volume size          %10"PRIu64" %s\n", hb.value, hb.unit);
+       exfat_humanize_bytes(total_space - avail_space, &hb);
+       printf("Used space           %10"PRIu64" %s\n", hb.value, hb.unit);
+       exfat_humanize_bytes(avail_space, &hb);
+       printf("Available space      %10"PRIu64" %s\n", hb.value, hb.unit);
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c
new file mode 100644 (file)
index 0000000..ad5accc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+       cbm.c (09.11.10)
+       Clusters Bitmap creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cbm.h"
+#include "fat.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <limits.h>
+#include <string.h>
+
+static off_t cbm_alignment(void)
+{
+       return get_cluster_size();
+}
+
+static off_t cbm_size(void)
+{
+       return DIV_ROUND_UP(
+                       (get_volume_size() - get_position(&cbm)) / get_cluster_size(),
+                       CHAR_BIT);
+}
+
+static int cbm_write(struct exfat_dev* dev)
+{
+       uint32_t allocated_clusters =
+                       DIV_ROUND_UP(cbm.get_size(), get_cluster_size()) +
+                       DIV_ROUND_UP(uct.get_size(), get_cluster_size()) +
+                       DIV_ROUND_UP(rootdir.get_size(), get_cluster_size());
+       size_t bitmap_size = ROUND_UP(allocated_clusters, CHAR_BIT);
+       bitmap_t* bitmap = malloc(BMAP_SIZE(bitmap_size));
+       size_t i;
+
+       if (bitmap == NULL)
+       {
+               exfat_error("failed to allocate bitmap of %zu bytes",
+                               BMAP_SIZE(bitmap_size));
+               return 1;
+       }
+       memset(bitmap, 0, BMAP_SIZE(bitmap_size));
+
+       for (i = 0; i < bitmap_size; i++)
+               if (i < allocated_clusters)
+                       BMAP_SET(bitmap, i);
+       if (exfat_write(dev, bitmap, bitmap_size / CHAR_BIT) < 0)
+       {
+               free(bitmap);
+               exfat_error("failed to write bitmap of %zu bytes",
+                               bitmap_size / CHAR_BIT);
+               return 1;
+       }
+       free(bitmap);
+       return 0;
+}
+
+const struct fs_object cbm =
+{
+       .get_alignment = cbm_alignment,
+       .get_size = cbm_size,
+       .write = cbm_write,
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h
new file mode 100644 (file)
index 0000000..3803b79
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       cbm.h (09.11.10)
+       Clusters Bitmap creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_CBM_H_INCLUDED
+#define MKFS_CBM_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object cbm;
+
+#endif /* ifndef MKFS_CBM_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c
new file mode 100644 (file)
index 0000000..c5174a7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+       fat.c (09.11.10)
+       File Allocation Table creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <unistd.h>
+
+static off_t fat_alignment(void)
+{
+       return (off_t) 128 * get_sector_size();
+}
+
+static off_t fat_size(void)
+{
+       return get_volume_size() / get_cluster_size() * sizeof(cluster_t);
+}
+
+static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster,
+               cluster_t value)
+{
+       le32_t fat_entry = cpu_to_le32(value);
+       if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0)
+       {
+               exfat_error("failed to write FAT entry 0x%x", value);
+               return 0;
+       }
+       return cluster + 1;
+}
+
+static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster,
+               uint64_t length)
+{
+       cluster_t end = cluster + DIV_ROUND_UP(length, get_cluster_size());
+
+       while (cluster < end - 1)
+       {
+               cluster = fat_write_entry(dev, cluster, cluster + 1);
+               if (cluster == 0)
+                       return 0;
+       }
+       return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END);
+}
+
+static int fat_write(struct exfat_dev* dev)
+{
+       cluster_t c = 0;
+
+       if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */
+               return 1;
+       if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */
+               return 1;
+       if (!(c = fat_write_entries(dev, c, cbm.get_size())))
+               return 1;
+       if (!(c = fat_write_entries(dev, c, uct.get_size())))
+               return 1;
+       if (!(c = fat_write_entries(dev, c, rootdir.get_size())))
+               return 1;
+
+       return 0;
+}
+
+const struct fs_object fat =
+{
+       .get_alignment = fat_alignment,
+       .get_size = fat_size,
+       .write = fat_write,
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h
new file mode 100644 (file)
index 0000000..fbecaa2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       fat.h (09.11.10)
+       File Allocation Table creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_FAT_H_INCLUDED
+#define MKFS_FAT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object fat;
+
+#endif /* ifndef MKFS_FAT_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c
new file mode 100644 (file)
index 0000000..10ecf23
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+       mkexfat.c (22.04.12)
+       FS creation engine.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "mkexfat.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_size(off_t volume_size)
+{
+       const struct fs_object** pp;
+       off_t position = 0;
+
+       for (pp = objects; *pp; pp++)
+       {
+               position = ROUND_UP(position, (*pp)->get_alignment());
+               position += (*pp)->get_size();
+       }
+
+       if (position > volume_size)
+       {
+               struct exfat_human_bytes vhb;
+
+               exfat_humanize_bytes(volume_size, &vhb);
+               exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit);
+               return 1;
+       }
+
+       return 0;
+
+}
+
+static int erase_object(struct exfat_dev* dev, const void* block,
+               size_t block_size, off_t start, off_t size)
+{
+       const off_t block_count = DIV_ROUND_UP(size, block_size);
+       off_t i;
+
+       if (exfat_seek(dev, start, SEEK_SET) == (off_t) -1)
+       {
+               exfat_error("seek to 0x%"PRIx64" failed", start);
+               return 1;
+       }
+       for (i = 0; i < size; i += block_size)
+       {
+               if (exfat_write(dev, block, MIN(size - i, block_size)) < 0)
+               {
+                       exfat_error("failed to erase block %"PRIu64"/%"PRIu64
+                                       " at 0x%"PRIx64, i + 1, block_count, start);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int erase(struct exfat_dev* dev)
+{
+       const struct fs_object** pp;
+       off_t position = 0;
+       const size_t block_size = 1024 * 1024;
+       void* block = malloc(block_size);
+
+       if (block == NULL)
+       {
+               exfat_error("failed to allocate erase block of %zu bytes", block_size);
+               return 1;
+       }
+       memset(block, 0, block_size);
+
+       for (pp = objects; *pp; pp++)
+       {
+               position = ROUND_UP(position, (*pp)->get_alignment());
+               if (erase_object(dev, block, block_size, position,
+                               (*pp)->get_size()) != 0)
+               {
+                       free(block);
+                       return 1;
+               }
+               position += (*pp)->get_size();
+       }
+
+       free(block);
+       return 0;
+}
+
+static int create(struct exfat_dev* dev)
+{
+       const struct fs_object** pp;
+       off_t position = 0;
+
+       for (pp = objects; *pp; pp++)
+       {
+               position = ROUND_UP(position, (*pp)->get_alignment());
+               if (exfat_seek(dev, position, SEEK_SET) == (off_t) -1)
+               {
+                       exfat_error("seek to 0x%"PRIx64" failed", position);
+                       return 1;
+               }
+               if ((*pp)->write(dev) != 0)
+                       return 1;
+               position += (*pp)->get_size();
+       }
+       return 0;
+}
+
+int mkfs(struct exfat_dev* dev, off_t volume_size)
+{
+       if (check_size(volume_size) != 0)
+               return 1;
+
+    exfat_debug("Creating... ");
+       //fputs("Creating... ", stdout);
+       //fflush(stdout);
+       if (erase(dev) != 0)
+               return 1;
+       if (create(dev) != 0)
+               return 1;
+       //puts("done.");
+
+       //fputs("Flushing... ", stdout);
+       //fflush(stdout);
+    exfat_debug("Flushing... ");
+       if (exfat_fsync(dev) != 0)
+               return 1;
+       //puts("done.");
+
+       return 0;
+}
+
+off_t get_position(const struct fs_object* object)
+{
+       const struct fs_object** pp;
+       off_t position = 0;
+
+       for (pp = objects; *pp; pp++)
+       {
+               position = ROUND_UP(position, (*pp)->get_alignment());
+               if (*pp == object)
+                       return position;
+               position += (*pp)->get_size();
+       }
+       exfat_bug("unknown object");
+
+    return 0;
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h
new file mode 100644 (file)
index 0000000..54816af
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+       mkexfat.h (09.11.10)
+       FS creation engine.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_MKEXFAT_H_INCLUDED
+#define MKFS_MKEXFAT_H_INCLUDED
+
+#include "exfat.h"
+
+struct fs_object
+{
+       off_t (*get_alignment)(void);
+       off_t (*get_size)(void);
+       int (*write)(struct exfat_dev* dev);
+};
+
+extern const struct fs_object* objects[];
+
+int get_sector_bits(void);
+int get_spc_bits(void);
+off_t get_volume_size(void);
+const le16_t* get_volume_label(void);
+uint32_t get_volume_serial(void);
+uint64_t get_first_sector(void);
+int get_sector_size(void);
+int get_cluster_size(void);
+
+int mkfs(struct exfat_dev* dev, off_t volume_size);
+off_t get_position(const struct fs_object* object);
+
+#endif /* ifndef MKFS_MKEXFAT_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c
new file mode 100644 (file)
index 0000000..c65035f
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+       main.c (15.08.10)
+       Creates exFAT file system.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "mkexfat.h"
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include "exfat.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+const struct fs_object* objects[] =
+{
+       &vbr,
+       &vbr,
+       &fat,
+       /* clusters heap */
+       &cbm,
+       &uct,
+       &rootdir,
+       NULL,
+};
+
+static struct
+{
+       int sector_bits;
+       int spc_bits;
+       off_t volume_size;
+       le16_t volume_label[EXFAT_ENAME_MAX + 1];
+       uint32_t volume_serial;
+       uint64_t first_sector;
+}
+param;
+
+extern int g_vtoy_exfat_disk_fd;
+extern uint64_t g_vtoy_exfat_part_size;
+
+int get_sector_bits(void)
+{
+       return param.sector_bits;
+}
+
+int get_spc_bits(void)
+{
+       return param.spc_bits;
+}
+
+off_t get_volume_size(void)
+{
+       return param.volume_size;
+}
+
+const le16_t* get_volume_label(void)
+{
+       return param.volume_label;
+}
+
+uint32_t get_volume_serial(void)
+{
+       return param.volume_serial;
+}
+
+uint64_t get_first_sector(void)
+{
+       return param.first_sector;
+}
+
+int get_sector_size(void)
+{
+       return 1 << get_sector_bits();
+}
+
+int get_cluster_size(void)
+{
+       return get_sector_size() << get_spc_bits();
+}
+
+static int setup_spc_bits(int sector_bits, int user_defined, off_t volume_size)
+{
+       int i;
+
+       if (user_defined != -1)
+       {
+               off_t cluster_size = 1 << sector_bits << user_defined;
+               if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER)
+               {
+                       struct exfat_human_bytes chb, vhb;
+
+                       exfat_humanize_bytes(cluster_size, &chb);
+                       exfat_humanize_bytes(volume_size, &vhb);
+                       exfat_error("cluster size %"PRIu64" %s is too small for "
+                                       "%"PRIu64" %s volume, try -s %d",
+                                       chb.value, chb.unit,
+                                       vhb.value, vhb.unit,
+                                       1 << setup_spc_bits(sector_bits, -1, volume_size));
+                       return -1;
+               }
+               return user_defined;
+       }
+
+       if (volume_size < 256ull * 1024 * 1024)
+               return MAX(0, 12 - sector_bits);        /* 4 KB */
+       if (volume_size < 32ull * 1024 * 1024 * 1024)
+               return MAX(0, 15 - sector_bits);        /* 32 KB */
+
+       for (i = 17; ; i++)                                             /* 128 KB or more */
+               if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
+                       return MAX(0, i - sector_bits);
+}
+
+static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s)
+{
+       memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t));
+       if (s == NULL)
+               return 0;
+       return utf8_to_utf16(label, s, EXFAT_ENAME_MAX + 1, strlen(s));
+}
+
+static uint32_t setup_volume_serial(uint32_t user_defined)
+{
+       struct timeval now;
+
+       if (user_defined != 0)
+               return user_defined;
+
+       if (gettimeofday(&now, NULL) != 0)
+       {
+               exfat_error("failed to form volume id");
+               return 0;
+       }
+       return (now.tv_sec << 20) | now.tv_usec;
+}
+
+static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits,
+               const char* volume_label, uint32_t volume_serial,
+               uint64_t first_sector)
+{
+       param.sector_bits = sector_bits;
+       param.first_sector = first_sector;
+       param.volume_size = exfat_get_size(dev);
+
+       param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size);
+       if (param.spc_bits == -1)
+               return 1;
+
+       if (setup_volume_label(param.volume_label, volume_label) != 0)
+               return 1;
+
+       param.volume_serial = setup_volume_serial(volume_serial);
+       if (param.volume_serial == 0)
+               return 1;
+
+       return mkfs(dev, param.volume_size);
+}
+
+static int logarithm2(int n)
+{
+       int i;
+
+       for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
+               if ((1 << i) == n)
+                       return i;
+       return -1;
+}
+
+static void usage(const char* prog)
+{
+       fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
+                       "[-p partition-first-sector] "
+                       "[-s sectors-per-cluster] [-V] <device>\n", prog);
+       exit(1);
+}
+
+int mkexfat_main(const char *devpath, int fd, uint64_t part_sector_count)
+{
+       int spc_bits = -1;
+       uint32_t volume_serial = 0;
+       uint64_t first_sector = 0;
+       struct exfat_dev* dev;
+
+#if 0
+       while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1)
+       {
+               switch (opt)
+               {
+               case 'i':
+                       volume_serial = strtol(optarg, NULL, 16);
+                       break;
+               case 'n':
+                       volume_label = optarg;
+                       break;
+               case 'p':
+                       first_sector = strtoll(optarg, NULL, 10);
+                       break;
+               case 's':
+                       spc_bits = logarithm2(atoi(optarg));
+                       if (spc_bits < 0)
+                       {
+                               exfat_error("invalid option value: '%s'", optarg);
+                               return 1;
+                       }
+                       break;
+               case 'V':
+                       puts("Copyright (C) 2011-2018  Andrew Nayenko");
+                       return 0;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+#endif /* #if 0 */
+
+    /* 
+     * DiskSize > 32GB  Cluster Size use 128KB
+     * DiskSize < 32GB  Cluster Size use 32KB 
+     */
+    if ((part_sector_count / 2097152) > 32)
+    {
+        spc_bits = logarithm2(256);
+    }
+    else
+    {
+        spc_bits = logarithm2(64);
+    }
+
+    g_vtoy_exfat_disk_fd = fd;
+    g_vtoy_exfat_part_size = part_sector_count * 512;
+    
+       dev = exfat_open(devpath, EXFAT_MODE_RW);
+       if (dev == NULL)
+               return 1;
+       if (setup(dev, 9, spc_bits, "Ventoy", volume_serial, first_sector) != 0)
+       {
+               exfat_close(dev);
+               return 1;
+       }
+       if (exfat_close(dev) != 0)
+               return 1;
+    
+       return 0;
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c
new file mode 100644 (file)
index 0000000..323da8a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+       rootdir.c (09.11.10)
+       Root directory creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "rootdir.h"
+#include "uct.h"
+#include "cbm.h"
+#include "uctc.h"
+#include <string.h>
+
+static off_t rootdir_alignment(void)
+{
+       return get_cluster_size();
+}
+
+static off_t rootdir_size(void)
+{
+       return get_cluster_size();
+}
+
+static void init_label_entry(struct exfat_entry_label* label_entry)
+{
+       memset(label_entry, 0, sizeof(struct exfat_entry_label));
+       label_entry->type = EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID;
+
+       if (utf16_length(get_volume_label()) == 0)
+               return;
+
+       memcpy(label_entry->name, get_volume_label(),
+                       EXFAT_ENAME_MAX * sizeof(le16_t));
+       label_entry->length = utf16_length(get_volume_label());
+       label_entry->type |= EXFAT_ENTRY_VALID;
+}
+
+static void init_bitmap_entry(struct exfat_entry_bitmap* bitmap_entry)
+{
+       memset(bitmap_entry, 0, sizeof(struct exfat_entry_bitmap));
+       bitmap_entry->type = EXFAT_ENTRY_BITMAP;
+       bitmap_entry->start_cluster = cpu_to_le32(EXFAT_FIRST_DATA_CLUSTER);
+       bitmap_entry->size = cpu_to_le64(cbm.get_size());
+}
+
+static void init_upcase_entry(struct exfat_entry_upcase* upcase_entry)
+{
+       size_t i;
+       uint32_t sum = 0;
+
+       for (i = 0; i < sizeof(upcase_table); i++)
+               sum = ((sum << 31) | (sum >> 1)) + upcase_table[i];
+
+       memset(upcase_entry, 0, sizeof(struct exfat_entry_upcase));
+       upcase_entry->type = EXFAT_ENTRY_UPCASE;
+       upcase_entry->checksum = cpu_to_le32(sum);
+       upcase_entry->start_cluster = cpu_to_le32(
+                       (get_position(&uct) - get_position(&cbm)) / get_cluster_size() +
+                       EXFAT_FIRST_DATA_CLUSTER);
+       upcase_entry->size = cpu_to_le64(sizeof(upcase_table));
+}
+
+static int rootdir_write(struct exfat_dev* dev)
+{
+       struct exfat_entry_label label_entry;
+       struct exfat_entry_bitmap bitmap_entry;
+       struct exfat_entry_upcase upcase_entry;
+
+       init_label_entry(&label_entry);
+       init_bitmap_entry(&bitmap_entry);
+       init_upcase_entry(&upcase_entry);
+
+       if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0)
+               return 1;
+       if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0)
+               return 1;
+       if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0)
+               return 1;
+       return 0;
+}
+
+const struct fs_object rootdir =
+{
+       .get_alignment = rootdir_alignment,
+       .get_size = rootdir_size,
+       .write = rootdir_write,
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h
new file mode 100644 (file)
index 0000000..87f437f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       rootdir.h (09.11.10)
+       Root directory creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_ROOTDIR_H_INCLUDED
+#define MKFS_ROOTDIR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object rootdir;
+
+#endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c
new file mode 100644 (file)
index 0000000..98cdce8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+       uct.c (09.11.10)
+       Upper Case Table creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uct.h"
+#include "uctc.h"
+
+static off_t uct_alignment(void)
+{
+       return get_cluster_size();
+}
+
+static off_t uct_size(void)
+{
+       return sizeof(upcase_table);
+}
+
+static int uct_write(struct exfat_dev* dev)
+{
+       if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0)
+       {
+               exfat_error("failed to write upcase table of %zu bytes",
+                               sizeof(upcase_table));
+               return 1;
+       }
+       return 0;
+}
+
+const struct fs_object uct =
+{
+       .get_alignment = uct_alignment,
+       .get_size = uct_size,
+       .write = uct_write,
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h
new file mode 100644 (file)
index 0000000..ce619ec
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       uct.h (09.11.10)
+       Upper Case Table creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCT_H_INCLUDED
+#define MKFS_UCT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object uct;
+
+#endif /* ifndef MKFS_UCT_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c
new file mode 100644 (file)
index 0000000..518219c
--- /dev/null
@@ -0,0 +1,757 @@
+/*
+       uctc.c (30.04.12)
+       Upper Case Table contents.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uctc.h"
+
+uint8_t upcase_table[5836] =
+{
+       0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+       0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
+       0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00,
+       0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00,
+       0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00,
+       0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+       0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00,
+       0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00,
+       0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00,
+       0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
+       0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00,
+       0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00,
+       0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,
+       0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
+       0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
+       0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
+       0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+       0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+       0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+       0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+       0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+       0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+       0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00,
+       0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00,
+       0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+       0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+       0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+       0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+       0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+       0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+       0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x7b, 0x00,
+       0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00,
+       0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
+       0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
+       0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x8b, 0x00,
+       0x8c, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00,
+       0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
+       0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
+       0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00,
+       0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00,
+       0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00,
+       0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00,
+       0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00,
+       0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00,
+       0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00,
+       0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00,
+       0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00,
+       0xbc, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00,
+       0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+       0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+       0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+       0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+       0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+       0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00,
+       0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+       0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00,
+       0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+       0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+       0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+       0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+       0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+       0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xf7, 0x00,
+       0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+       0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0x78, 0x01,
+       0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01,
+       0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
+       0x08, 0x01, 0x08, 0x01, 0x0a, 0x01, 0x0a, 0x01,
+       0x0c, 0x01, 0x0c, 0x01, 0x0e, 0x01, 0x0e, 0x01,
+       0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01,
+       0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
+       0x18, 0x01, 0x18, 0x01, 0x1a, 0x01, 0x1a, 0x01,
+       0x1c, 0x01, 0x1c, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+       0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01,
+       0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
+       0x28, 0x01, 0x28, 0x01, 0x2a, 0x01, 0x2a, 0x01,
+       0x2c, 0x01, 0x2c, 0x01, 0x2e, 0x01, 0x2e, 0x01,
+       0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01,
+       0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
+       0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3b, 0x01,
+       0x3b, 0x01, 0x3d, 0x01, 0x3d, 0x01, 0x3f, 0x01,
+       0x3f, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01,
+       0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
+       0x47, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4a, 0x01,
+       0x4c, 0x01, 0x4c, 0x01, 0x4e, 0x01, 0x4e, 0x01,
+       0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01,
+       0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
+       0x58, 0x01, 0x58, 0x01, 0x5a, 0x01, 0x5a, 0x01,
+       0x5c, 0x01, 0x5c, 0x01, 0x5e, 0x01, 0x5e, 0x01,
+       0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01,
+       0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
+       0x68, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x6a, 0x01,
+       0x6c, 0x01, 0x6c, 0x01, 0x6e, 0x01, 0x6e, 0x01,
+       0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01,
+       0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
+       0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7b, 0x01,
+       0x7b, 0x01, 0x7d, 0x01, 0x7d, 0x01, 0x7f, 0x01,
+       0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01,
+       0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
+       0x87, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01,
+       0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01,
+       0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01,
+       0x94, 0x01, 0xf6, 0x01, 0x96, 0x01, 0x97, 0x01,
+       0x98, 0x01, 0x98, 0x01, 0x3d, 0x02, 0x9b, 0x01,
+       0x9c, 0x01, 0x9d, 0x01, 0x20, 0x02, 0x9f, 0x01,
+       0xa0, 0x01, 0xa0, 0x01, 0xa2, 0x01, 0xa2, 0x01,
+       0xa4, 0x01, 0xa4, 0x01, 0xa6, 0x01, 0xa7, 0x01,
+       0xa7, 0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01,
+       0xac, 0x01, 0xac, 0x01, 0xae, 0x01, 0xaf, 0x01,
+       0xaf, 0x01, 0xb1, 0x01, 0xb2, 0x01, 0xb3, 0x01,
+       0xb3, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0xb7, 0x01,
+       0xb8, 0x01, 0xb8, 0x01, 0xba, 0x01, 0xbb, 0x01,
+       0xbc, 0x01, 0xbc, 0x01, 0xbe, 0x01, 0xf7, 0x01,
+       0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0xc3, 0x01,
+       0xc4, 0x01, 0xc5, 0x01, 0xc4, 0x01, 0xc7, 0x01,
+       0xc8, 0x01, 0xc7, 0x01, 0xca, 0x01, 0xcb, 0x01,
+       0xca, 0x01, 0xcd, 0x01, 0xcd, 0x01, 0xcf, 0x01,
+       0xcf, 0x01, 0xd1, 0x01, 0xd1, 0x01, 0xd3, 0x01,
+       0xd3, 0x01, 0xd5, 0x01, 0xd5, 0x01, 0xd7, 0x01,
+       0xd7, 0x01, 0xd9, 0x01, 0xd9, 0x01, 0xdb, 0x01,
+       0xdb, 0x01, 0x8e, 0x01, 0xde, 0x01, 0xde, 0x01,
+       0xe0, 0x01, 0xe0, 0x01, 0xe2, 0x01, 0xe2, 0x01,
+       0xe4, 0x01, 0xe4, 0x01, 0xe6, 0x01, 0xe6, 0x01,
+       0xe8, 0x01, 0xe8, 0x01, 0xea, 0x01, 0xea, 0x01,
+       0xec, 0x01, 0xec, 0x01, 0xee, 0x01, 0xee, 0x01,
+       0xf0, 0x01, 0xf1, 0x01, 0xf2, 0x01, 0xf1, 0x01,
+       0xf4, 0x01, 0xf4, 0x01, 0xf6, 0x01, 0xf7, 0x01,
+       0xf8, 0x01, 0xf8, 0x01, 0xfa, 0x01, 0xfa, 0x01,
+       0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x01, 0xfe, 0x01,
+       0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
+       0x08, 0x02, 0x08, 0x02, 0x0a, 0x02, 0x0a, 0x02,
+       0x0c, 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x0e, 0x02,
+       0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02,
+       0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
+       0x18, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1a, 0x02,
+       0x1c, 0x02, 0x1c, 0x02, 0x1e, 0x02, 0x1e, 0x02,
+       0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02,
+       0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
+       0x28, 0x02, 0x28, 0x02, 0x2a, 0x02, 0x2a, 0x02,
+       0x2c, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x2e, 0x02,
+       0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02,
+       0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
+       0x38, 0x02, 0x39, 0x02, 0x65, 0x2c, 0x3b, 0x02,
+       0x3b, 0x02, 0x3d, 0x02, 0x66, 0x2c, 0x3f, 0x02,
+       0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02,
+       0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
+       0x48, 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4a, 0x02,
+       0x4c, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x4e, 0x02,
+       0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01,
+       0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8a, 0x01,
+       0x58, 0x02, 0x8f, 0x01, 0x5a, 0x02, 0x90, 0x01,
+       0x5c, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x5f, 0x02,
+       0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01,
+       0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
+       0x97, 0x01, 0x96, 0x01, 0x6a, 0x02, 0x62, 0x2c,
+       0x6c, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x9c, 0x01,
+       0x70, 0x02, 0x71, 0x02, 0x9d, 0x01, 0x73, 0x02,
+       0x74, 0x02, 0x9f, 0x01, 0x76, 0x02, 0x77, 0x02,
+       0x78, 0x02, 0x79, 0x02, 0x7a, 0x02, 0x7b, 0x02,
+       0x7c, 0x02, 0x64, 0x2c, 0x7e, 0x02, 0x7f, 0x02,
+       0xa6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xa9, 0x01,
+       0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
+       0xae, 0x01, 0x44, 0x02, 0xb1, 0x01, 0xb2, 0x01,
+       0x45, 0x02, 0x8d, 0x02, 0x8e, 0x02, 0x8f, 0x02,
+       0x90, 0x02, 0x91, 0x02, 0xb7, 0x01, 0x93, 0x02,
+       0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
+       0x98, 0x02, 0x99, 0x02, 0x9a, 0x02, 0x9b, 0x02,
+       0x9c, 0x02, 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x02,
+       0xa0, 0x02, 0xa1, 0x02, 0xa2, 0x02, 0xa3, 0x02,
+       0xa4, 0x02, 0xa5, 0x02, 0xa6, 0x02, 0xa7, 0x02,
+       0xa8, 0x02, 0xa9, 0x02, 0xaa, 0x02, 0xab, 0x02,
+       0xac, 0x02, 0xad, 0x02, 0xae, 0x02, 0xaf, 0x02,
+       0xb0, 0x02, 0xb1, 0x02, 0xb2, 0x02, 0xb3, 0x02,
+       0xb4, 0x02, 0xb5, 0x02, 0xb6, 0x02, 0xb7, 0x02,
+       0xb8, 0x02, 0xb9, 0x02, 0xba, 0x02, 0xbb, 0x02,
+       0xbc, 0x02, 0xbd, 0x02, 0xbe, 0x02, 0xbf, 0x02,
+       0xc0, 0x02, 0xc1, 0x02, 0xc2, 0x02, 0xc3, 0x02,
+       0xc4, 0x02, 0xc5, 0x02, 0xc6, 0x02, 0xc7, 0x02,
+       0xc8, 0x02, 0xc9, 0x02, 0xca, 0x02, 0xcb, 0x02,
+       0xcc, 0x02, 0xcd, 0x02, 0xce, 0x02, 0xcf, 0x02,
+       0xd0, 0x02, 0xd1, 0x02, 0xd2, 0x02, 0xd3, 0x02,
+       0xd4, 0x02, 0xd5, 0x02, 0xd6, 0x02, 0xd7, 0x02,
+       0xd8, 0x02, 0xd9, 0x02, 0xda, 0x02, 0xdb, 0x02,
+       0xdc, 0x02, 0xdd, 0x02, 0xde, 0x02, 0xdf, 0x02,
+       0xe0, 0x02, 0xe1, 0x02, 0xe2, 0x02, 0xe3, 0x02,
+       0xe4, 0x02, 0xe5, 0x02, 0xe6, 0x02, 0xe7, 0x02,
+       0xe8, 0x02, 0xe9, 0x02, 0xea, 0x02, 0xeb, 0x02,
+       0xec, 0x02, 0xed, 0x02, 0xee, 0x02, 0xef, 0x02,
+       0xf0, 0x02, 0xf1, 0x02, 0xf2, 0x02, 0xf3, 0x02,
+       0xf4, 0x02, 0xf5, 0x02, 0xf6, 0x02, 0xf7, 0x02,
+       0xf8, 0x02, 0xf9, 0x02, 0xfa, 0x02, 0xfb, 0x02,
+       0xfc, 0x02, 0xfd, 0x02, 0xfe, 0x02, 0xff, 0x02,
+       0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03,
+       0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
+       0x08, 0x03, 0x09, 0x03, 0x0a, 0x03, 0x0b, 0x03,
+       0x0c, 0x03, 0x0d, 0x03, 0x0e, 0x03, 0x0f, 0x03,
+       0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03,
+       0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
+       0x18, 0x03, 0x19, 0x03, 0x1a, 0x03, 0x1b, 0x03,
+       0x1c, 0x03, 0x1d, 0x03, 0x1e, 0x03, 0x1f, 0x03,
+       0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03,
+       0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
+       0x28, 0x03, 0x29, 0x03, 0x2a, 0x03, 0x2b, 0x03,
+       0x2c, 0x03, 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03,
+       0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03,
+       0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
+       0x38, 0x03, 0x39, 0x03, 0x3a, 0x03, 0x3b, 0x03,
+       0x3c, 0x03, 0x3d, 0x03, 0x3e, 0x03, 0x3f, 0x03,
+       0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03,
+       0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
+       0x48, 0x03, 0x49, 0x03, 0x4a, 0x03, 0x4b, 0x03,
+       0x4c, 0x03, 0x4d, 0x03, 0x4e, 0x03, 0x4f, 0x03,
+       0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03,
+       0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
+       0x58, 0x03, 0x59, 0x03, 0x5a, 0x03, 0x5b, 0x03,
+       0x5c, 0x03, 0x5d, 0x03, 0x5e, 0x03, 0x5f, 0x03,
+       0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03,
+       0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
+       0x68, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x03,
+       0x6c, 0x03, 0x6d, 0x03, 0x6e, 0x03, 0x6f, 0x03,
+       0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03,
+       0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
+       0x78, 0x03, 0x79, 0x03, 0x7a, 0x03, 0xfd, 0x03,
+       0xfe, 0x03, 0xff, 0x03, 0x7e, 0x03, 0x7f, 0x03,
+       0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03,
+       0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
+       0x88, 0x03, 0x89, 0x03, 0x8a, 0x03, 0x8b, 0x03,
+       0x8c, 0x03, 0x8d, 0x03, 0x8e, 0x03, 0x8f, 0x03,
+       0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+       0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+       0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+       0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+       0xa0, 0x03, 0xa1, 0x03, 0xa2, 0x03, 0xa3, 0x03,
+       0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+       0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+       0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8a, 0x03,
+       0xb0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+       0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+       0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+       0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+       0xa0, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa3, 0x03,
+       0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+       0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+       0x8c, 0x03, 0x8e, 0x03, 0x8f, 0x03, 0xcf, 0x03,
+       0xd0, 0x03, 0xd1, 0x03, 0xd2, 0x03, 0xd3, 0x03,
+       0xd4, 0x03, 0xd5, 0x03, 0xd6, 0x03, 0xd7, 0x03,
+       0xd8, 0x03, 0xd8, 0x03, 0xda, 0x03, 0xda, 0x03,
+       0xdc, 0x03, 0xdc, 0x03, 0xde, 0x03, 0xde, 0x03,
+       0xe0, 0x03, 0xe0, 0x03, 0xe2, 0x03, 0xe2, 0x03,
+       0xe4, 0x03, 0xe4, 0x03, 0xe6, 0x03, 0xe6, 0x03,
+       0xe8, 0x03, 0xe8, 0x03, 0xea, 0x03, 0xea, 0x03,
+       0xec, 0x03, 0xec, 0x03, 0xee, 0x03, 0xee, 0x03,
+       0xf0, 0x03, 0xf1, 0x03, 0xf9, 0x03, 0xf3, 0x03,
+       0xf4, 0x03, 0xf5, 0x03, 0xf6, 0x03, 0xf7, 0x03,
+       0xf7, 0x03, 0xf9, 0x03, 0xfa, 0x03, 0xfa, 0x03,
+       0xfc, 0x03, 0xfd, 0x03, 0xfe, 0x03, 0xff, 0x03,
+       0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+       0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+       0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+       0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+       0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+       0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+       0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+       0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+       0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+       0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+       0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+       0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+       0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+       0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+       0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+       0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+       0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+       0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+       0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+       0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+       0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+       0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+       0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+       0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+       0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04,
+       0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
+       0x68, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6a, 0x04,
+       0x6c, 0x04, 0x6c, 0x04, 0x6e, 0x04, 0x6e, 0x04,
+       0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04,
+       0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
+       0x78, 0x04, 0x78, 0x04, 0x7a, 0x04, 0x7a, 0x04,
+       0x7c, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x7e, 0x04,
+       0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04,
+       0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
+       0x88, 0x04, 0x89, 0x04, 0x8a, 0x04, 0x8a, 0x04,
+       0x8c, 0x04, 0x8c, 0x04, 0x8e, 0x04, 0x8e, 0x04,
+       0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04,
+       0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
+       0x98, 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9a, 0x04,
+       0x9c, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0x9e, 0x04,
+       0xa0, 0x04, 0xa0, 0x04, 0xa2, 0x04, 0xa2, 0x04,
+       0xa4, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa6, 0x04,
+       0xa8, 0x04, 0xa8, 0x04, 0xaa, 0x04, 0xaa, 0x04,
+       0xac, 0x04, 0xac, 0x04, 0xae, 0x04, 0xae, 0x04,
+       0xb0, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb2, 0x04,
+       0xb4, 0x04, 0xb4, 0x04, 0xb6, 0x04, 0xb6, 0x04,
+       0xb8, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xba, 0x04,
+       0xbc, 0x04, 0xbc, 0x04, 0xbe, 0x04, 0xbe, 0x04,
+       0xc0, 0x04, 0xc1, 0x04, 0xc1, 0x04, 0xc3, 0x04,
+       0xc3, 0x04, 0xc5, 0x04, 0xc5, 0x04, 0xc7, 0x04,
+       0xc7, 0x04, 0xc9, 0x04, 0xc9, 0x04, 0xcb, 0x04,
+       0xcb, 0x04, 0xcd, 0x04, 0xcd, 0x04, 0xc0, 0x04,
+       0xd0, 0x04, 0xd0, 0x04, 0xd2, 0x04, 0xd2, 0x04,
+       0xd4, 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd6, 0x04,
+       0xd8, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xda, 0x04,
+       0xdc, 0x04, 0xdc, 0x04, 0xde, 0x04, 0xde, 0x04,
+       0xe0, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe2, 0x04,
+       0xe4, 0x04, 0xe4, 0x04, 0xe6, 0x04, 0xe6, 0x04,
+       0xe8, 0x04, 0xe8, 0x04, 0xea, 0x04, 0xea, 0x04,
+       0xec, 0x04, 0xec, 0x04, 0xee, 0x04, 0xee, 0x04,
+       0xf0, 0x04, 0xf0, 0x04, 0xf2, 0x04, 0xf2, 0x04,
+       0xf4, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf6, 0x04,
+       0xf8, 0x04, 0xf8, 0x04, 0xfa, 0x04, 0xfa, 0x04,
+       0xfc, 0x04, 0xfc, 0x04, 0xfe, 0x04, 0xfe, 0x04,
+       0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05,
+       0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
+       0x08, 0x05, 0x08, 0x05, 0x0a, 0x05, 0x0a, 0x05,
+       0x0c, 0x05, 0x0c, 0x05, 0x0e, 0x05, 0x0e, 0x05,
+       0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05,
+       0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
+       0x18, 0x05, 0x19, 0x05, 0x1a, 0x05, 0x1b, 0x05,
+       0x1c, 0x05, 0x1d, 0x05, 0x1e, 0x05, 0x1f, 0x05,
+       0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05,
+       0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
+       0x28, 0x05, 0x29, 0x05, 0x2a, 0x05, 0x2b, 0x05,
+       0x2c, 0x05, 0x2d, 0x05, 0x2e, 0x05, 0x2f, 0x05,
+       0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+       0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+       0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+       0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+       0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+       0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+       0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+       0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+       0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+       0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
+       0x58, 0x05, 0x59, 0x05, 0x5a, 0x05, 0x5b, 0x05,
+       0x5c, 0x05, 0x5d, 0x05, 0x5e, 0x05, 0x5f, 0x05,
+       0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+       0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+       0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+       0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+       0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+       0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+       0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+       0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+       0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+       0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xff, 0xff,
+       0xf6, 0x17, 0x63, 0x2c, 0x7e, 0x1d, 0x7f, 0x1d,
+       0x80, 0x1d, 0x81, 0x1d, 0x82, 0x1d, 0x83, 0x1d,
+       0x84, 0x1d, 0x85, 0x1d, 0x86, 0x1d, 0x87, 0x1d,
+       0x88, 0x1d, 0x89, 0x1d, 0x8a, 0x1d, 0x8b, 0x1d,
+       0x8c, 0x1d, 0x8d, 0x1d, 0x8e, 0x1d, 0x8f, 0x1d,
+       0x90, 0x1d, 0x91, 0x1d, 0x92, 0x1d, 0x93, 0x1d,
+       0x94, 0x1d, 0x95, 0x1d, 0x96, 0x1d, 0x97, 0x1d,
+       0x98, 0x1d, 0x99, 0x1d, 0x9a, 0x1d, 0x9b, 0x1d,
+       0x9c, 0x1d, 0x9d, 0x1d, 0x9e, 0x1d, 0x9f, 0x1d,
+       0xa0, 0x1d, 0xa1, 0x1d, 0xa2, 0x1d, 0xa3, 0x1d,
+       0xa4, 0x1d, 0xa5, 0x1d, 0xa6, 0x1d, 0xa7, 0x1d,
+       0xa8, 0x1d, 0xa9, 0x1d, 0xaa, 0x1d, 0xab, 0x1d,
+       0xac, 0x1d, 0xad, 0x1d, 0xae, 0x1d, 0xaf, 0x1d,
+       0xb0, 0x1d, 0xb1, 0x1d, 0xb2, 0x1d, 0xb3, 0x1d,
+       0xb4, 0x1d, 0xb5, 0x1d, 0xb6, 0x1d, 0xb7, 0x1d,
+       0xb8, 0x1d, 0xb9, 0x1d, 0xba, 0x1d, 0xbb, 0x1d,
+       0xbc, 0x1d, 0xbd, 0x1d, 0xbe, 0x1d, 0xbf, 0x1d,
+       0xc0, 0x1d, 0xc1, 0x1d, 0xc2, 0x1d, 0xc3, 0x1d,
+       0xc4, 0x1d, 0xc5, 0x1d, 0xc6, 0x1d, 0xc7, 0x1d,
+       0xc8, 0x1d, 0xc9, 0x1d, 0xca, 0x1d, 0xcb, 0x1d,
+       0xcc, 0x1d, 0xcd, 0x1d, 0xce, 0x1d, 0xcf, 0x1d,
+       0xd0, 0x1d, 0xd1, 0x1d, 0xd2, 0x1d, 0xd3, 0x1d,
+       0xd4, 0x1d, 0xd5, 0x1d, 0xd6, 0x1d, 0xd7, 0x1d,
+       0xd8, 0x1d, 0xd9, 0x1d, 0xda, 0x1d, 0xdb, 0x1d,
+       0xdc, 0x1d, 0xdd, 0x1d, 0xde, 0x1d, 0xdf, 0x1d,
+       0xe0, 0x1d, 0xe1, 0x1d, 0xe2, 0x1d, 0xe3, 0x1d,
+       0xe4, 0x1d, 0xe5, 0x1d, 0xe6, 0x1d, 0xe7, 0x1d,
+       0xe8, 0x1d, 0xe9, 0x1d, 0xea, 0x1d, 0xeb, 0x1d,
+       0xec, 0x1d, 0xed, 0x1d, 0xee, 0x1d, 0xef, 0x1d,
+       0xf0, 0x1d, 0xf1, 0x1d, 0xf2, 0x1d, 0xf3, 0x1d,
+       0xf4, 0x1d, 0xf5, 0x1d, 0xf6, 0x1d, 0xf7, 0x1d,
+       0xf8, 0x1d, 0xf9, 0x1d, 0xfa, 0x1d, 0xfb, 0x1d,
+       0xfc, 0x1d, 0xfd, 0x1d, 0xfe, 0x1d, 0xff, 0x1d,
+       0x00, 0x1e, 0x00, 0x1e, 0x02, 0x1e, 0x02, 0x1e,
+       0x04, 0x1e, 0x04, 0x1e, 0x06, 0x1e, 0x06, 0x1e,
+       0x08, 0x1e, 0x08, 0x1e, 0x0a, 0x1e, 0x0a, 0x1e,
+       0x0c, 0x1e, 0x0c, 0x1e, 0x0e, 0x1e, 0x0e, 0x1e,
+       0x10, 0x1e, 0x10, 0x1e, 0x12, 0x1e, 0x12, 0x1e,
+       0x14, 0x1e, 0x14, 0x1e, 0x16, 0x1e, 0x16, 0x1e,
+       0x18, 0x1e, 0x18, 0x1e, 0x1a, 0x1e, 0x1a, 0x1e,
+       0x1c, 0x1e, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+       0x20, 0x1e, 0x20, 0x1e, 0x22, 0x1e, 0x22, 0x1e,
+       0x24, 0x1e, 0x24, 0x1e, 0x26, 0x1e, 0x26, 0x1e,
+       0x28, 0x1e, 0x28, 0x1e, 0x2a, 0x1e, 0x2a, 0x1e,
+       0x2c, 0x1e, 0x2c, 0x1e, 0x2e, 0x1e, 0x2e, 0x1e,
+       0x30, 0x1e, 0x30, 0x1e, 0x32, 0x1e, 0x32, 0x1e,
+       0x34, 0x1e, 0x34, 0x1e, 0x36, 0x1e, 0x36, 0x1e,
+       0x38, 0x1e, 0x38, 0x1e, 0x3a, 0x1e, 0x3a, 0x1e,
+       0x3c, 0x1e, 0x3c, 0x1e, 0x3e, 0x1e, 0x3e, 0x1e,
+       0x40, 0x1e, 0x40, 0x1e, 0x42, 0x1e, 0x42, 0x1e,
+       0x44, 0x1e, 0x44, 0x1e, 0x46, 0x1e, 0x46, 0x1e,
+       0x48, 0x1e, 0x48, 0x1e, 0x4a, 0x1e, 0x4a, 0x1e,
+       0x4c, 0x1e, 0x4c, 0x1e, 0x4e, 0x1e, 0x4e, 0x1e,
+       0x50, 0x1e, 0x50, 0x1e, 0x52, 0x1e, 0x52, 0x1e,
+       0x54, 0x1e, 0x54, 0x1e, 0x56, 0x1e, 0x56, 0x1e,
+       0x58, 0x1e, 0x58, 0x1e, 0x5a, 0x1e, 0x5a, 0x1e,
+       0x5c, 0x1e, 0x5c, 0x1e, 0x5e, 0x1e, 0x5e, 0x1e,
+       0x60, 0x1e, 0x60, 0x1e, 0x62, 0x1e, 0x62, 0x1e,
+       0x64, 0x1e, 0x64, 0x1e, 0x66, 0x1e, 0x66, 0x1e,
+       0x68, 0x1e, 0x68, 0x1e, 0x6a, 0x1e, 0x6a, 0x1e,
+       0x6c, 0x1e, 0x6c, 0x1e, 0x6e, 0x1e, 0x6e, 0x1e,
+       0x70, 0x1e, 0x70, 0x1e, 0x72, 0x1e, 0x72, 0x1e,
+       0x74, 0x1e, 0x74, 0x1e, 0x76, 0x1e, 0x76, 0x1e,
+       0x78, 0x1e, 0x78, 0x1e, 0x7a, 0x1e, 0x7a, 0x1e,
+       0x7c, 0x1e, 0x7c, 0x1e, 0x7e, 0x1e, 0x7e, 0x1e,
+       0x80, 0x1e, 0x80, 0x1e, 0x82, 0x1e, 0x82, 0x1e,
+       0x84, 0x1e, 0x84, 0x1e, 0x86, 0x1e, 0x86, 0x1e,
+       0x88, 0x1e, 0x88, 0x1e, 0x8a, 0x1e, 0x8a, 0x1e,
+       0x8c, 0x1e, 0x8c, 0x1e, 0x8e, 0x1e, 0x8e, 0x1e,
+       0x90, 0x1e, 0x90, 0x1e, 0x92, 0x1e, 0x92, 0x1e,
+       0x94, 0x1e, 0x94, 0x1e, 0x96, 0x1e, 0x97, 0x1e,
+       0x98, 0x1e, 0x99, 0x1e, 0x9a, 0x1e, 0x9b, 0x1e,
+       0x9c, 0x1e, 0x9d, 0x1e, 0x9e, 0x1e, 0x9f, 0x1e,
+       0xa0, 0x1e, 0xa0, 0x1e, 0xa2, 0x1e, 0xa2, 0x1e,
+       0xa4, 0x1e, 0xa4, 0x1e, 0xa6, 0x1e, 0xa6, 0x1e,
+       0xa8, 0x1e, 0xa8, 0x1e, 0xaa, 0x1e, 0xaa, 0x1e,
+       0xac, 0x1e, 0xac, 0x1e, 0xae, 0x1e, 0xae, 0x1e,
+       0xb0, 0x1e, 0xb0, 0x1e, 0xb2, 0x1e, 0xb2, 0x1e,
+       0xb4, 0x1e, 0xb4, 0x1e, 0xb6, 0x1e, 0xb6, 0x1e,
+       0xb8, 0x1e, 0xb8, 0x1e, 0xba, 0x1e, 0xba, 0x1e,
+       0xbc, 0x1e, 0xbc, 0x1e, 0xbe, 0x1e, 0xbe, 0x1e,
+       0xc0, 0x1e, 0xc0, 0x1e, 0xc2, 0x1e, 0xc2, 0x1e,
+       0xc4, 0x1e, 0xc4, 0x1e, 0xc6, 0x1e, 0xc6, 0x1e,
+       0xc8, 0x1e, 0xc8, 0x1e, 0xca, 0x1e, 0xca, 0x1e,
+       0xcc, 0x1e, 0xcc, 0x1e, 0xce, 0x1e, 0xce, 0x1e,
+       0xd0, 0x1e, 0xd0, 0x1e, 0xd2, 0x1e, 0xd2, 0x1e,
+       0xd4, 0x1e, 0xd4, 0x1e, 0xd6, 0x1e, 0xd6, 0x1e,
+       0xd8, 0x1e, 0xd8, 0x1e, 0xda, 0x1e, 0xda, 0x1e,
+       0xdc, 0x1e, 0xdc, 0x1e, 0xde, 0x1e, 0xde, 0x1e,
+       0xe0, 0x1e, 0xe0, 0x1e, 0xe2, 0x1e, 0xe2, 0x1e,
+       0xe4, 0x1e, 0xe4, 0x1e, 0xe6, 0x1e, 0xe6, 0x1e,
+       0xe8, 0x1e, 0xe8, 0x1e, 0xea, 0x1e, 0xea, 0x1e,
+       0xec, 0x1e, 0xec, 0x1e, 0xee, 0x1e, 0xee, 0x1e,
+       0xf0, 0x1e, 0xf0, 0x1e, 0xf2, 0x1e, 0xf2, 0x1e,
+       0xf4, 0x1e, 0xf4, 0x1e, 0xf6, 0x1e, 0xf6, 0x1e,
+       0xf8, 0x1e, 0xf8, 0x1e, 0xfa, 0x1e, 0xfb, 0x1e,
+       0xfc, 0x1e, 0xfd, 0x1e, 0xfe, 0x1e, 0xff, 0x1e,
+       0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+       0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+       0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+       0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+       0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+       0x1c, 0x1f, 0x1d, 0x1f, 0x16, 0x1f, 0x17, 0x1f,
+       0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+       0x1c, 0x1f, 0x1d, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f,
+       0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+       0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+       0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+       0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+       0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+       0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+       0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+       0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+       0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+       0x4c, 0x1f, 0x4d, 0x1f, 0x46, 0x1f, 0x47, 0x1f,
+       0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+       0x4c, 0x1f, 0x4d, 0x1f, 0x4e, 0x1f, 0x4f, 0x1f,
+       0x50, 0x1f, 0x59, 0x1f, 0x52, 0x1f, 0x5b, 0x1f,
+       0x54, 0x1f, 0x5d, 0x1f, 0x56, 0x1f, 0x5f, 0x1f,
+       0x58, 0x1f, 0x59, 0x1f, 0x5a, 0x1f, 0x5b, 0x1f,
+       0x5c, 0x1f, 0x5d, 0x1f, 0x5e, 0x1f, 0x5f, 0x1f,
+       0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+       0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+       0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+       0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+       0xba, 0x1f, 0xbb, 0x1f, 0xc8, 0x1f, 0xc9, 0x1f,
+       0xca, 0x1f, 0xcb, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+       0xf8, 0x1f, 0xf9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+       0xfa, 0x1f, 0xfb, 0x1f, 0x7e, 0x1f, 0x7f, 0x1f,
+       0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+       0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+       0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+       0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+       0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+       0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+       0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+       0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+       0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+       0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+       0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+       0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+       0xb8, 0x1f, 0xb9, 0x1f, 0xb2, 0x1f, 0xbc, 0x1f,
+       0xb4, 0x1f, 0xb5, 0x1f, 0xb6, 0x1f, 0xb7, 0x1f,
+       0xb8, 0x1f, 0xb9, 0x1f, 0xba, 0x1f, 0xbb, 0x1f,
+       0xbc, 0x1f, 0xbd, 0x1f, 0xbe, 0x1f, 0xbf, 0x1f,
+       0xc0, 0x1f, 0xc1, 0x1f, 0xc2, 0x1f, 0xc3, 0x1f,
+       0xc4, 0x1f, 0xc5, 0x1f, 0xc6, 0x1f, 0xc7, 0x1f,
+       0xc8, 0x1f, 0xc9, 0x1f, 0xca, 0x1f, 0xcb, 0x1f,
+       0xc3, 0x1f, 0xcd, 0x1f, 0xce, 0x1f, 0xcf, 0x1f,
+       0xd8, 0x1f, 0xd9, 0x1f, 0xd2, 0x1f, 0xd3, 0x1f,
+       0xd4, 0x1f, 0xd5, 0x1f, 0xd6, 0x1f, 0xd7, 0x1f,
+       0xd8, 0x1f, 0xd9, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+       0xdc, 0x1f, 0xdd, 0x1f, 0xde, 0x1f, 0xdf, 0x1f,
+       0xe8, 0x1f, 0xe9, 0x1f, 0xe2, 0x1f, 0xe3, 0x1f,
+       0xe4, 0x1f, 0xec, 0x1f, 0xe6, 0x1f, 0xe7, 0x1f,
+       0xe8, 0x1f, 0xe9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+       0xec, 0x1f, 0xed, 0x1f, 0xee, 0x1f, 0xef, 0x1f,
+       0xf0, 0x1f, 0xf1, 0x1f, 0xf2, 0x1f, 0xf3, 0x1f,
+       0xf4, 0x1f, 0xf5, 0x1f, 0xf6, 0x1f, 0xf7, 0x1f,
+       0xf8, 0x1f, 0xf9, 0x1f, 0xfa, 0x1f, 0xfb, 0x1f,
+       0xf3, 0x1f, 0xfd, 0x1f, 0xfe, 0x1f, 0xff, 0x1f,
+       0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
+       0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20,
+       0x08, 0x20, 0x09, 0x20, 0x0a, 0x20, 0x0b, 0x20,
+       0x0c, 0x20, 0x0d, 0x20, 0x0e, 0x20, 0x0f, 0x20,
+       0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
+       0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20,
+       0x18, 0x20, 0x19, 0x20, 0x1a, 0x20, 0x1b, 0x20,
+       0x1c, 0x20, 0x1d, 0x20, 0x1e, 0x20, 0x1f, 0x20,
+       0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
+       0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20,
+       0x28, 0x20, 0x29, 0x20, 0x2a, 0x20, 0x2b, 0x20,
+       0x2c, 0x20, 0x2d, 0x20, 0x2e, 0x20, 0x2f, 0x20,
+       0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
+       0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20,
+       0x38, 0x20, 0x39, 0x20, 0x3a, 0x20, 0x3b, 0x20,
+       0x3c, 0x20, 0x3d, 0x20, 0x3e, 0x20, 0x3f, 0x20,
+       0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
+       0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20,
+       0x48, 0x20, 0x49, 0x20, 0x4a, 0x20, 0x4b, 0x20,
+       0x4c, 0x20, 0x4d, 0x20, 0x4e, 0x20, 0x4f, 0x20,
+       0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
+       0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20,
+       0x58, 0x20, 0x59, 0x20, 0x5a, 0x20, 0x5b, 0x20,
+       0x5c, 0x20, 0x5d, 0x20, 0x5e, 0x20, 0x5f, 0x20,
+       0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
+       0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20,
+       0x68, 0x20, 0x69, 0x20, 0x6a, 0x20, 0x6b, 0x20,
+       0x6c, 0x20, 0x6d, 0x20, 0x6e, 0x20, 0x6f, 0x20,
+       0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
+       0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20,
+       0x78, 0x20, 0x79, 0x20, 0x7a, 0x20, 0x7b, 0x20,
+       0x7c, 0x20, 0x7d, 0x20, 0x7e, 0x20, 0x7f, 0x20,
+       0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
+       0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20,
+       0x88, 0x20, 0x89, 0x20, 0x8a, 0x20, 0x8b, 0x20,
+       0x8c, 0x20, 0x8d, 0x20, 0x8e, 0x20, 0x8f, 0x20,
+       0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
+       0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20,
+       0x98, 0x20, 0x99, 0x20, 0x9a, 0x20, 0x9b, 0x20,
+       0x9c, 0x20, 0x9d, 0x20, 0x9e, 0x20, 0x9f, 0x20,
+       0xa0, 0x20, 0xa1, 0x20, 0xa2, 0x20, 0xa3, 0x20,
+       0xa4, 0x20, 0xa5, 0x20, 0xa6, 0x20, 0xa7, 0x20,
+       0xa8, 0x20, 0xa9, 0x20, 0xaa, 0x20, 0xab, 0x20,
+       0xac, 0x20, 0xad, 0x20, 0xae, 0x20, 0xaf, 0x20,
+       0xb0, 0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20,
+       0xb4, 0x20, 0xb5, 0x20, 0xb6, 0x20, 0xb7, 0x20,
+       0xb8, 0x20, 0xb9, 0x20, 0xba, 0x20, 0xbb, 0x20,
+       0xbc, 0x20, 0xbd, 0x20, 0xbe, 0x20, 0xbf, 0x20,
+       0xc0, 0x20, 0xc1, 0x20, 0xc2, 0x20, 0xc3, 0x20,
+       0xc4, 0x20, 0xc5, 0x20, 0xc6, 0x20, 0xc7, 0x20,
+       0xc8, 0x20, 0xc9, 0x20, 0xca, 0x20, 0xcb, 0x20,
+       0xcc, 0x20, 0xcd, 0x20, 0xce, 0x20, 0xcf, 0x20,
+       0xd0, 0x20, 0xd1, 0x20, 0xd2, 0x20, 0xd3, 0x20,
+       0xd4, 0x20, 0xd5, 0x20, 0xd6, 0x20, 0xd7, 0x20,
+       0xd8, 0x20, 0xd9, 0x20, 0xda, 0x20, 0xdb, 0x20,
+       0xdc, 0x20, 0xdd, 0x20, 0xde, 0x20, 0xdf, 0x20,
+       0xe0, 0x20, 0xe1, 0x20, 0xe2, 0x20, 0xe3, 0x20,
+       0xe4, 0x20, 0xe5, 0x20, 0xe6, 0x20, 0xe7, 0x20,
+       0xe8, 0x20, 0xe9, 0x20, 0xea, 0x20, 0xeb, 0x20,
+       0xec, 0x20, 0xed, 0x20, 0xee, 0x20, 0xef, 0x20,
+       0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, 0x20,
+       0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20,
+       0xf8, 0x20, 0xf9, 0x20, 0xfa, 0x20, 0xfb, 0x20,
+       0xfc, 0x20, 0xfd, 0x20, 0xfe, 0x20, 0xff, 0x20,
+       0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
+       0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21,
+       0x08, 0x21, 0x09, 0x21, 0x0a, 0x21, 0x0b, 0x21,
+       0x0c, 0x21, 0x0d, 0x21, 0x0e, 0x21, 0x0f, 0x21,
+       0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
+       0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21,
+       0x18, 0x21, 0x19, 0x21, 0x1a, 0x21, 0x1b, 0x21,
+       0x1c, 0x21, 0x1d, 0x21, 0x1e, 0x21, 0x1f, 0x21,
+       0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
+       0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21,
+       0x28, 0x21, 0x29, 0x21, 0x2a, 0x21, 0x2b, 0x21,
+       0x2c, 0x21, 0x2d, 0x21, 0x2e, 0x21, 0x2f, 0x21,
+       0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
+       0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21,
+       0x38, 0x21, 0x39, 0x21, 0x3a, 0x21, 0x3b, 0x21,
+       0x3c, 0x21, 0x3d, 0x21, 0x3e, 0x21, 0x3f, 0x21,
+       0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
+       0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21,
+       0x48, 0x21, 0x49, 0x21, 0x4a, 0x21, 0x4b, 0x21,
+       0x4c, 0x21, 0x4d, 0x21, 0x32, 0x21, 0x4f, 0x21,
+       0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
+       0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21,
+       0x58, 0x21, 0x59, 0x21, 0x5a, 0x21, 0x5b, 0x21,
+       0x5c, 0x21, 0x5d, 0x21, 0x5e, 0x21, 0x5f, 0x21,
+       0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+       0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+       0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+       0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+       0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+       0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+       0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+       0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+       0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
+       0x83, 0x21, 0xff, 0xff, 0x4b, 0x03, 0xb6, 0x24,
+       0xb7, 0x24, 0xb8, 0x24, 0xb9, 0x24, 0xba, 0x24,
+       0xbb, 0x24, 0xbc, 0x24, 0xbd, 0x24, 0xbe, 0x24,
+       0xbf, 0x24, 0xc0, 0x24, 0xc1, 0x24, 0xc2, 0x24,
+       0xc3, 0x24, 0xc4, 0x24, 0xc5, 0x24, 0xc6, 0x24,
+       0xc7, 0x24, 0xc8, 0x24, 0xc9, 0x24, 0xca, 0x24,
+       0xcb, 0x24, 0xcc, 0x24, 0xcd, 0x24, 0xce, 0x24,
+       0xcf, 0x24, 0xff, 0xff, 0x46, 0x07, 0x00, 0x2c,
+       0x01, 0x2c, 0x02, 0x2c, 0x03, 0x2c, 0x04, 0x2c,
+       0x05, 0x2c, 0x06, 0x2c, 0x07, 0x2c, 0x08, 0x2c,
+       0x09, 0x2c, 0x0a, 0x2c, 0x0b, 0x2c, 0x0c, 0x2c,
+       0x0d, 0x2c, 0x0e, 0x2c, 0x0f, 0x2c, 0x10, 0x2c,
+       0x11, 0x2c, 0x12, 0x2c, 0x13, 0x2c, 0x14, 0x2c,
+       0x15, 0x2c, 0x16, 0x2c, 0x17, 0x2c, 0x18, 0x2c,
+       0x19, 0x2c, 0x1a, 0x2c, 0x1b, 0x2c, 0x1c, 0x2c,
+       0x1d, 0x2c, 0x1e, 0x2c, 0x1f, 0x2c, 0x20, 0x2c,
+       0x21, 0x2c, 0x22, 0x2c, 0x23, 0x2c, 0x24, 0x2c,
+       0x25, 0x2c, 0x26, 0x2c, 0x27, 0x2c, 0x28, 0x2c,
+       0x29, 0x2c, 0x2a, 0x2c, 0x2b, 0x2c, 0x2c, 0x2c,
+       0x2d, 0x2c, 0x2e, 0x2c, 0x5f, 0x2c, 0x60, 0x2c,
+       0x60, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c,
+       0x65, 0x2c, 0x66, 0x2c, 0x67, 0x2c, 0x67, 0x2c,
+       0x69, 0x2c, 0x69, 0x2c, 0x6b, 0x2c, 0x6b, 0x2c,
+       0x6d, 0x2c, 0x6e, 0x2c, 0x6f, 0x2c, 0x70, 0x2c,
+       0x71, 0x2c, 0x72, 0x2c, 0x73, 0x2c, 0x74, 0x2c,
+       0x75, 0x2c, 0x75, 0x2c, 0x77, 0x2c, 0x78, 0x2c,
+       0x79, 0x2c, 0x7a, 0x2c, 0x7b, 0x2c, 0x7c, 0x2c,
+       0x7d, 0x2c, 0x7e, 0x2c, 0x7f, 0x2c, 0x80, 0x2c,
+       0x80, 0x2c, 0x82, 0x2c, 0x82, 0x2c, 0x84, 0x2c,
+       0x84, 0x2c, 0x86, 0x2c, 0x86, 0x2c, 0x88, 0x2c,
+       0x88, 0x2c, 0x8a, 0x2c, 0x8a, 0x2c, 0x8c, 0x2c,
+       0x8c, 0x2c, 0x8e, 0x2c, 0x8e, 0x2c, 0x90, 0x2c,
+       0x90, 0x2c, 0x92, 0x2c, 0x92, 0x2c, 0x94, 0x2c,
+       0x94, 0x2c, 0x96, 0x2c, 0x96, 0x2c, 0x98, 0x2c,
+       0x98, 0x2c, 0x9a, 0x2c, 0x9a, 0x2c, 0x9c, 0x2c,
+       0x9c, 0x2c, 0x9e, 0x2c, 0x9e, 0x2c, 0xa0, 0x2c,
+       0xa0, 0x2c, 0xa2, 0x2c, 0xa2, 0x2c, 0xa4, 0x2c,
+       0xa4, 0x2c, 0xa6, 0x2c, 0xa6, 0x2c, 0xa8, 0x2c,
+       0xa8, 0x2c, 0xaa, 0x2c, 0xaa, 0x2c, 0xac, 0x2c,
+       0xac, 0x2c, 0xae, 0x2c, 0xae, 0x2c, 0xb0, 0x2c,
+       0xb0, 0x2c, 0xb2, 0x2c, 0xb2, 0x2c, 0xb4, 0x2c,
+       0xb4, 0x2c, 0xb6, 0x2c, 0xb6, 0x2c, 0xb8, 0x2c,
+       0xb8, 0x2c, 0xba, 0x2c, 0xba, 0x2c, 0xbc, 0x2c,
+       0xbc, 0x2c, 0xbe, 0x2c, 0xbe, 0x2c, 0xc0, 0x2c,
+       0xc0, 0x2c, 0xc2, 0x2c, 0xc2, 0x2c, 0xc4, 0x2c,
+       0xc4, 0x2c, 0xc6, 0x2c, 0xc6, 0x2c, 0xc8, 0x2c,
+       0xc8, 0x2c, 0xca, 0x2c, 0xca, 0x2c, 0xcc, 0x2c,
+       0xcc, 0x2c, 0xce, 0x2c, 0xce, 0x2c, 0xd0, 0x2c,
+       0xd0, 0x2c, 0xd2, 0x2c, 0xd2, 0x2c, 0xd4, 0x2c,
+       0xd4, 0x2c, 0xd6, 0x2c, 0xd6, 0x2c, 0xd8, 0x2c,
+       0xd8, 0x2c, 0xda, 0x2c, 0xda, 0x2c, 0xdc, 0x2c,
+       0xdc, 0x2c, 0xde, 0x2c, 0xde, 0x2c, 0xe0, 0x2c,
+       0xe0, 0x2c, 0xe2, 0x2c, 0xe2, 0x2c, 0xe4, 0x2c,
+       0xe5, 0x2c, 0xe6, 0x2c, 0xe7, 0x2c, 0xe8, 0x2c,
+       0xe9, 0x2c, 0xea, 0x2c, 0xeb, 0x2c, 0xec, 0x2c,
+       0xed, 0x2c, 0xee, 0x2c, 0xef, 0x2c, 0xf0, 0x2c,
+       0xf1, 0x2c, 0xf2, 0x2c, 0xf3, 0x2c, 0xf4, 0x2c,
+       0xf5, 0x2c, 0xf6, 0x2c, 0xf7, 0x2c, 0xf8, 0x2c,
+       0xf9, 0x2c, 0xfa, 0x2c, 0xfb, 0x2c, 0xfc, 0x2c,
+       0xfd, 0x2c, 0xfe, 0x2c, 0xff, 0x2c, 0xa0, 0x10,
+       0xa1, 0x10, 0xa2, 0x10, 0xa3, 0x10, 0xa4, 0x10,
+       0xa5, 0x10, 0xa6, 0x10, 0xa7, 0x10, 0xa8, 0x10,
+       0xa9, 0x10, 0xaa, 0x10, 0xab, 0x10, 0xac, 0x10,
+       0xad, 0x10, 0xae, 0x10, 0xaf, 0x10, 0xb0, 0x10,
+       0xb1, 0x10, 0xb2, 0x10, 0xb3, 0x10, 0xb4, 0x10,
+       0xb5, 0x10, 0xb6, 0x10, 0xb7, 0x10, 0xb8, 0x10,
+       0xb9, 0x10, 0xba, 0x10, 0xbb, 0x10, 0xbc, 0x10,
+       0xbd, 0x10, 0xbe, 0x10, 0xbf, 0x10, 0xc0, 0x10,
+       0xc1, 0x10, 0xc2, 0x10, 0xc3, 0x10, 0xc4, 0x10,
+       0xc5, 0x10, 0xff, 0xff, 0x1b, 0xd2, 0x21, 0xff,
+       0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
+       0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff,
+       0x2a, 0xff, 0x2b, 0xff, 0x2c, 0xff, 0x2d, 0xff,
+       0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
+       0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff,
+       0x36, 0xff, 0x37, 0xff, 0x38, 0xff, 0x39, 0xff,
+       0x3a, 0xff, 0x5b, 0xff, 0x5c, 0xff, 0x5d, 0xff,
+       0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
+       0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff,
+       0x66, 0xff, 0x67, 0xff, 0x68, 0xff, 0x69, 0xff,
+       0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
+       0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff,
+       0x72, 0xff, 0x73, 0xff, 0x74, 0xff, 0x75, 0xff,
+       0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
+       0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff,
+       0x7e, 0xff, 0x7f, 0xff, 0x80, 0xff, 0x81, 0xff,
+       0x82, 0xff, 0x83, 0xff, 0x84, 0xff, 0x85, 0xff,
+       0x86, 0xff, 0x87, 0xff, 0x88, 0xff, 0x89, 0xff,
+       0x8a, 0xff, 0x8b, 0xff, 0x8c, 0xff, 0x8d, 0xff,
+       0x8e, 0xff, 0x8f, 0xff, 0x90, 0xff, 0x91, 0xff,
+       0x92, 0xff, 0x93, 0xff, 0x94, 0xff, 0x95, 0xff,
+       0x96, 0xff, 0x97, 0xff, 0x98, 0xff, 0x99, 0xff,
+       0x9a, 0xff, 0x9b, 0xff, 0x9c, 0xff, 0x9d, 0xff,
+       0x9e, 0xff, 0x9f, 0xff, 0xa0, 0xff, 0xa1, 0xff,
+       0xa2, 0xff, 0xa3, 0xff, 0xa4, 0xff, 0xa5, 0xff,
+       0xa6, 0xff, 0xa7, 0xff, 0xa8, 0xff, 0xa9, 0xff,
+       0xaa, 0xff, 0xab, 0xff, 0xac, 0xff, 0xad, 0xff,
+       0xae, 0xff, 0xaf, 0xff, 0xb0, 0xff, 0xb1, 0xff,
+       0xb2, 0xff, 0xb3, 0xff, 0xb4, 0xff, 0xb5, 0xff,
+       0xb6, 0xff, 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xff,
+       0xba, 0xff, 0xbb, 0xff, 0xbc, 0xff, 0xbd, 0xff,
+       0xbe, 0xff, 0xbf, 0xff, 0xc0, 0xff, 0xc1, 0xff,
+       0xc2, 0xff, 0xc3, 0xff, 0xc4, 0xff, 0xc5, 0xff,
+       0xc6, 0xff, 0xc7, 0xff, 0xc8, 0xff, 0xc9, 0xff,
+       0xca, 0xff, 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0xff,
+       0xce, 0xff, 0xcf, 0xff, 0xd0, 0xff, 0xd1, 0xff,
+       0xd2, 0xff, 0xd3, 0xff, 0xd4, 0xff, 0xd5, 0xff,
+       0xd6, 0xff, 0xd7, 0xff, 0xd8, 0xff, 0xd9, 0xff,
+       0xda, 0xff, 0xdb, 0xff, 0xdc, 0xff, 0xdd, 0xff,
+       0xde, 0xff, 0xdf, 0xff, 0xe0, 0xff, 0xe1, 0xff,
+       0xe2, 0xff, 0xe3, 0xff, 0xe4, 0xff, 0xe5, 0xff,
+       0xe6, 0xff, 0xe7, 0xff, 0xe8, 0xff, 0xe9, 0xff,
+       0xea, 0xff, 0xeb, 0xff, 0xec, 0xff, 0xed, 0xff,
+       0xee, 0xff, 0xef, 0xff, 0xf0, 0xff, 0xf1, 0xff,
+       0xf2, 0xff, 0xf3, 0xff, 0xf4, 0xff, 0xf5, 0xff,
+       0xf6, 0xff, 0xf7, 0xff, 0xf8, 0xff, 0xf9, 0xff,
+       0xfa, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xfd, 0xff,
+       0xfe, 0xff, 0xff, 0xff
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h
new file mode 100644 (file)
index 0000000..bd5ebb2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       uctc.h (30.10.10)
+       Upper Case Table declaration.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCTC_H_INCLUDED
+#define MKFS_UCTC_H_INCLUDED
+
+#include <stdint.h>
+
+extern uint8_t upcase_table[5836];
+
+#endif /* ifndef MKFS_UCTC_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c
new file mode 100644 (file)
index 0000000..e40081f
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+       vbr.c (09.11.10)
+       Volume Boot Record creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+#include <string.h>
+
+static off_t vbr_alignment(void)
+{
+       return get_sector_size();
+}
+
+static off_t vbr_size(void)
+{
+       return 12 * get_sector_size();
+}
+
+static void init_sb(struct exfat_super_block* sb)
+{
+       uint32_t clusters_max;
+       uint32_t fat_sectors;
+
+       clusters_max = get_volume_size() / get_cluster_size();
+       fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t),
+                       get_sector_size());
+
+       memset(sb, 0, sizeof(struct exfat_super_block));
+       sb->jump[0] = 0xeb;
+       sb->jump[1] = 0x76;
+       sb->jump[2] = 0x90;
+       memcpy(sb->oem_name, "EXFAT   ", sizeof(sb->oem_name));
+       sb->sector_start = cpu_to_le64(get_first_sector());
+       sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size());
+       sb->fat_sector_start = cpu_to_le32(
+                       fat.get_alignment() / get_sector_size());
+       sb->fat_sector_count = cpu_to_le32(ROUND_UP(
+                       le32_to_cpu(sb->fat_sector_start) + fat_sectors,
+                               1 << get_spc_bits()) -
+                       le32_to_cpu(sb->fat_sector_start));
+       sb->cluster_sector_start = cpu_to_le32(
+                       get_position(&cbm) / get_sector_size());
+       sb->cluster_count = cpu_to_le32(clusters_max -
+                       ((le32_to_cpu(sb->fat_sector_start) +
+                         le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits()));
+       sb->rootdir_cluster = cpu_to_le32(
+                       (get_position(&rootdir) - get_position(&cbm)) / get_cluster_size()
+                       + EXFAT_FIRST_DATA_CLUSTER);
+       sb->volume_serial = cpu_to_le32(get_volume_serial());
+       sb->version.major = 1;
+       sb->version.minor = 0;
+       sb->volume_state = cpu_to_le16(0);
+       sb->sector_bits = get_sector_bits();
+       sb->spc_bits = get_spc_bits();
+       sb->fat_count = 1;
+       sb->drive_no = 0x80;
+       sb->allocated_percent = 0;
+       sb->boot_signature = cpu_to_le16(0xaa55);
+}
+
+static int vbr_write(struct exfat_dev* dev)
+{
+       struct exfat_super_block sb;
+       uint32_t checksum;
+       le32_t* sector = malloc(get_sector_size());
+       size_t i;
+
+       if (sector == NULL)
+       {
+               exfat_error("failed to allocate sector-sized block of memory");
+               return 1;
+       }
+
+       init_sb(&sb);
+       if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0)
+       {
+               free(sector);
+               exfat_error("failed to write super block sector");
+               return 1;
+       }
+       checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block));
+
+       memset(sector, 0, get_sector_size());
+       sector[get_sector_size() / sizeof(sector[0]) - 1] =
+                       cpu_to_le32(0xaa550000);
+       for (i = 0; i < 8; i++)
+       {
+               if (exfat_write(dev, sector, get_sector_size()) < 0)
+               {
+                       free(sector);
+                       exfat_error("failed to write a sector with boot signature");
+                       return 1;
+               }
+               checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+       }
+
+       memset(sector, 0, get_sector_size());
+       for (i = 0; i < 2; i++)
+       {
+               if (exfat_write(dev, sector, get_sector_size()) < 0)
+               {
+                       free(sector);
+                       exfat_error("failed to write an empty sector");
+                       return 1;
+               }
+               checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+       }
+
+       for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++)
+               sector[i] = cpu_to_le32(checksum);
+       if (exfat_write(dev, sector, get_sector_size()) < 0)
+       {
+               free(sector);
+               exfat_error("failed to write checksum sector");
+               return 1;
+       }
+
+       free(sector);
+       return 0;
+}
+
+const struct fs_object vbr =
+{
+       .get_alignment = vbr_alignment,
+       .get_size = vbr_size,
+       .write = vbr_write,
+};
diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h
new file mode 100644 (file)
index 0000000..c1f59a6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+       vbr.h (09.11.10)
+       Volume Boot Record creation code.
+
+       Free exFAT implementation.
+       Copyright (C) 2011-2018  Andrew Nayenko
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_VBR_H_INCLUDED
+#define MKFS_VBR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object vbr;
+
+#endif /* ifndef MKFS_VBR_H_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh
new file mode 100644 (file)
index 0000000..dec432f
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+rm -rf include
+rm -rf lib
+
+cd release
+gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_64.a *.o
+rm -f *.o
+
+
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_32.a *.o
+rm -f *.o
+
+
+aarch64-linux-gnu-gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_aa64.a *.o
+rm -f *.o
+
+
+cd -
+
+
+mkdir lib
+mkdir include
+
+mv release/*.a lib/
+cp -a release/*.h include/
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h
new file mode 100644 (file)
index 0000000..a40a28f
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#define FILE_CREATE         (1 << 5)
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h
new file mode 100644 (file)
index 0000000..ac4dc86
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        #define FAT_PRINTF(a)               printf a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a
new file mode 100644 (file)
index 0000000..e68fa38
Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a differ
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a
new file mode 100644 (file)
index 0000000..8598a87
Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a differ
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a
new file mode 100644 (file)
index 0000000..7b9b66c
Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a differ
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt
new file mode 100644 (file)
index 0000000..61fc164
--- /dev/null
@@ -0,0 +1,22 @@
+File IO Lib API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+void fl_init(void)\r
+\r
+  Called to initialize FAT IO library.\r
+  This should be called prior to any other functions.\r
+\r
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))\r
+\r
+  [Optional] File system thread safety locking functions.\r
+  For thread safe operation, you should provide lock() and unlock() functions.\r
+  Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.\r
+\r
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+  This function is used to attach system specific disk/media access functions.  \r
+  This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).\r
+\r
+void fl_shutdown(void)\r
+\r
+  Shutdown the FAT IO library. This purges any un-saved data back to disk.\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..4335f7f
--- /dev/null
@@ -0,0 +1,345 @@
+                   GNU GENERAL PUBLIC LICENSE\r
+                      Version 2, June 1991\r
+\r
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\r
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+                           Preamble\r
+\r
+  The licenses for most software are designed to take away your\r
+freedom to share and change it.  By contrast, the GNU General Public\r
+License is intended to guarantee your freedom to share and change free\r
+software--to make sure the software is free for all its users.  This\r
+General Public License applies to most of the Free Software\r
+Foundation's software and to any other program whose authors commit to\r
+using it.  (Some other Free Software Foundation software is covered by\r
+the GNU Library General Public License instead.)  You can apply it to\r
+your programs, too.\r
+\r
+  When we speak of free software, we are referring to freedom, not\r
+price.  Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+this service if you wish), that you receive source code or can get it\r
+if you want it, that you can change the software or use pieces of it\r
+in new free programs; and that you know you can do these things.\r
+\r
+  To protect your rights, we need to make restrictions that forbid\r
+anyone to deny you these rights or to ask you to surrender the rights.\r
+These restrictions translate to certain responsibilities for you if you\r
+distribute copies of the software, or if you modify it.\r
+\r
+  For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must give the recipients all the rights that\r
+you have.  You must make sure that they, too, receive or can get the\r
+source code.  And you must show them these terms so they know their\r
+rights.\r
+\r
+  We protect your rights with two steps: (1) copyright the software, and\r
+(2) offer you this license which gives you legal permission to copy,\r
+distribute and/or modify the software.\r
+\r
+  Also, for each author's protection and ours, we want to make certain\r
+that everyone understands that there is no warranty for this free\r
+software.  If the software is modified by someone else and passed on, we\r
+want its recipients to know that what they have is not the original, so\r
+that any problems introduced by others will not reflect on the original\r
+authors' reputations.\r
+\r
+  Finally, any free program is threatened constantly by software\r
+patents.  We wish to avoid the danger that redistributors of a free\r
+program will individually obtain patent licenses, in effect making the\r
+program proprietary.  To prevent this, we have made it clear that any\r
+patent must be licensed for everyone's free use or not licensed at all.\r
+\r
+  The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+\r
+                   GNU GENERAL PUBLIC LICENSE\r
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+\r
+  0. This License applies to any program or other work which contains\r
+a notice placed by the copyright holder saying it may be distributed\r
+under the terms of this General Public License.  The "Program", below,\r
+refers to any such program or work, and a "work based on the Program"\r
+means either the Program or any derivative work under copyright law:\r
+that is to say, a work containing the Program or a portion of it,\r
+either verbatim or with modifications and/or translated into another\r
+language.  (Hereinafter, translation is included without limitation in\r
+the term "modification".)  Each licensee is addressed as "you".\r
+\r
+Activities other than copying, distribution and modification are not\r
+covered by this License; they are outside its scope.  The act of\r
+running the Program is not restricted, and the output from the Program\r
+is covered only if its contents constitute a work based on the\r
+Program (independent of having been made by running the Program).\r
+Whether that is true depends on what the Program does.\r
+\r
+  1. You may copy and distribute verbatim copies of the Program's\r
+source code as you receive it, in any medium, provided that you\r
+conspicuously and appropriately publish on each copy an appropriate\r
+copyright notice and disclaimer of warranty; keep intact all the\r
+notices that refer to this License and to the absence of any warranty;\r
+and give any other recipients of the Program a copy of this License\r
+along with the Program.\r
+\r
+You may charge a fee for the physical act of transferring a copy, and\r
+you may at your option offer warranty protection in exchange for a fee.\r
+\r
+  2. You may modify your copy or copies of the Program or any portion\r
+of it, thus forming a work based on the Program, and copy and\r
+distribute such modifications or work under the terms of Section 1\r
+above, provided that you also meet all of these conditions:\r
+\r
+    a) You must cause the modified files to carry prominent notices\r
+    stating that you changed the files and the date of any change.\r
+\r
+    b) You must cause any work that you distribute or publish, that in\r
+    whole or in part contains or is derived from the Program or any\r
+    part thereof, to be licensed as a whole at no charge to all third\r
+    parties under the terms of this License.\r
+\r
+    c) If the modified program normally reads commands interactively\r
+    when run, you must cause it, when started running for such\r
+    interactive use in the most ordinary way, to print or display an\r
+    announcement including an appropriate copyright notice and a\r
+    notice that there is no warranty (or else, saying that you provide\r
+    a warranty) and that users may redistribute the program under\r
+    these conditions, and telling the user how to view a copy of this\r
+    License.  (Exception: if the Program itself is interactive but\r
+    does not normally print such an announcement, your work based on\r
+    the Program is not required to print an announcement.)\r
+\r
+\r
+These requirements apply to the modified work as a whole.  If\r
+identifiable sections of that work are not derived from the Program,\r
+and can be reasonably considered independent and separate works in\r
+themselves, then this License, and its terms, do not apply to those\r
+sections when you distribute them as separate works.  But when you\r
+distribute the same sections as part of a whole which is a work based\r
+on the Program, the distribution of the whole must be on the terms of\r
+this License, whose permissions for other licensees extend to the\r
+entire whole, and thus to each and every part regardless of who wrote it.\r
+\r
+Thus, it is not the intent of this section to claim rights or contest\r
+your rights to work written entirely by you; rather, the intent is to\r
+exercise the right to control the distribution of derivative or\r
+collective works based on the Program.\r
+\r
+In addition, mere aggregation of another work not based on the Program\r
+with the Program (or with a work based on the Program) on a volume of\r
+a storage or distribution medium does not bring the other work under\r
+the scope of this License.\r
+\r
+  3. You may copy and distribute the Program (or a work based on it,\r
+under Section 2) in object code or executable form under the terms of\r
+Sections 1 and 2 above provided that you also do one of the following:\r
+\r
+    a) Accompany it with the complete corresponding machine-readable\r
+    source code, which must be distributed under the terms of Sections\r
+    1 and 2 above on a medium customarily used for software interchange; or,\r
+\r
+    b) Accompany it with a written offer, valid for at least three\r
+    years, to give any third party, for a charge no more than your\r
+    cost of physically performing source distribution, a complete\r
+    machine-readable copy of the corresponding source code, to be\r
+    distributed under the terms of Sections 1 and 2 above on a medium\r
+    customarily used for software interchange; or,\r
+\r
+    c) Accompany it with the information you received as to the offer\r
+    to distribute corresponding source code.  (This alternative is\r
+    allowed only for noncommercial distribution and only if you\r
+    received the program in object code or executable form with such\r
+    an offer, in accord with Subsection b above.)\r
+\r
+The source code for a work means the preferred form of the work for\r
+making modifications to it.  For an executable work, complete source\r
+code means all the source code for all modules it contains, plus any\r
+associated interface definition files, plus the scripts used to\r
+control compilation and installation of the executable.  However, as a\r
+special exception, the source code distributed need not include\r
+anything that is normally distributed (in either source or binary\r
+form) with the major components (compiler, kernel, and so on) of the\r
+operating system on which the executable runs, unless that component\r
+itself accompanies the executable.\r
+\r
+If distribution of executable or object code is made by offering\r
+access to copy from a designated place, then offering equivalent\r
+access to copy the source code from the same place counts as\r
+distribution of the source code, even though third parties are not\r
+compelled to copy the source along with the object code.\r
+\r
+\r
+  4. You may not copy, modify, sublicense, or distribute the Program\r
+except as expressly provided under this License.  Any attempt\r
+otherwise to copy, modify, sublicense or distribute the Program is\r
+void, and will automatically terminate your rights under this License.\r
+However, parties who have received copies, or rights, from you under\r
+this License will not have their licenses terminated so long as such\r
+parties remain in full compliance.\r
+\r
+  5. You are not required to accept this License, since you have not\r
+signed it.  However, nothing else grants you permission to modify or\r
+distribute the Program or its derivative works.  These actions are\r
+prohibited by law if you do not accept this License.  Therefore, by\r
+modifying or distributing the Program (or any work based on the\r
+Program), you indicate your acceptance of this License to do so, and\r
+all its terms and conditions for copying, distributing or modifying\r
+the Program or works based on it.\r
+\r
+  6. Each time you redistribute the Program (or any work based on the\r
+Program), the recipient automatically receives a license from the\r
+original licensor to copy, distribute or modify the Program subject to\r
+these terms and conditions.  You may not impose any further\r
+restrictions on the recipients' exercise of the rights granted herein.\r
+You are not responsible for enforcing compliance by third parties to\r
+this License.\r
+\r
+  7. If, as a consequence of a court judgment or allegation of patent\r
+infringement or for any other reason (not limited to patent issues),\r
+conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License.  If you cannot\r
+distribute so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you\r
+may not distribute the Program at all.  For example, if a patent\r
+license would not permit royalty-free redistribution of the Program by\r
+all those who receive copies directly or indirectly through you, then\r
+the only way you could satisfy both it and this License would be to\r
+refrain entirely from distribution of the Program.\r
+\r
+If any portion of this section is held invalid or unenforceable under\r
+any particular circumstance, the balance of the section is intended to\r
+apply and the section as a whole is intended to apply in other\r
+circumstances.\r
+\r
+It is not the purpose of this section to induce you to infringe any\r
+patents or other property right claims or to contest validity of any\r
+such claims; this section has the sole purpose of protecting the\r
+integrity of the free software distribution system, which is\r
+implemented by public license practices.  Many people have made\r
+generous contributions to the wide range of software distributed\r
+through that system in reliance on consistent application of that\r
+system; it is up to the author/donor to decide if he or she is willing\r
+to distribute software through any other system and a licensee cannot\r
+impose that choice.\r
+\r
+This section is intended to make thoroughly clear what is believed to\r
+be a consequence of the rest of this License.\r
+\r
+\r
+  8. If the distribution and/or use of the Program is restricted in\r
+certain countries either by patents or by copyrighted interfaces, the\r
+original copyright holder who places the Program under this License\r
+may add an explicit geographical distribution limitation excluding\r
+those countries, so that distribution is permitted only in or among\r
+countries not thus excluded.  In such case, this License incorporates\r
+the limitation as if written in the body of this License.\r
+\r
+  9. The Free Software Foundation may publish revised and/or new versions\r
+of the General Public License from time to time.  Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+Each version is given a distinguishing version number.  If the Program\r
+specifies a version number of this License which applies to it and "any\r
+later version", you have the option of following the terms and conditions\r
+either of that version or of any later version published by the Free\r
+Software Foundation.  If the Program does not specify a version number of\r
+this License, you may choose any version ever published by the Free Software\r
+Foundation.\r
+\r
+  10. If you wish to incorporate parts of the Program into other free\r
+programs whose distribution conditions are different, write to the author\r
+to ask for permission.  For software which is copyrighted by the Free\r
+Software Foundation, write to the Free Software Foundation; we sometimes\r
+make exceptions for this.  Our decision will be guided by the two goals\r
+of preserving the free status of all derivatives of our free software and\r
+of promoting the sharing and reuse of software generally.\r
+\r
+                           NO WARRANTY\r
+\r
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\r
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\r
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\r
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
+REPAIR OR CORRECTION.\r
+\r
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGES.\r
+\r
+                    END OF TERMS AND CONDITIONS\r
+\r
+\r
+           How to Apply These Terms to Your New Programs\r
+\r
+  If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+  To do so, attach the following notices to the program.  It is safest\r
+to attach them to the start of each source file to most effectively\r
+convey the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+    <one line to give the program's name and a brief idea of what it does.>\r
+    Copyright (C) <year>  <name of author>\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License\r
+    along with this program; if not, write to the Free Software\r
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+If the program is interactive, make it output a short notice like this\r
+when it starts in an interactive mode:\r
+\r
+    Gnomovision version 69, Copyright (C) year name of author\r
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+    This is free software, and you are welcome to redistribute it\r
+    under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License.  Of course, the commands you use may\r
+be called something other than `show w' and `show c'; they could even be\r
+mouse-clicks or menu items--whatever suits your program.\r
+\r
+You should also get your employer (if you work as a programmer) or your\r
+school, if any, to sign a "copyright disclaimer" for the program, if\r
+necessary.  Here is a sample; alter the names:\r
+\r
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
+\r
+  <signature of Ty Coon>, 1 April 1989\r
+  Ty Coon, President of Vice\r
+\r
+This General Public License does not permit incorporating your program into\r
+proprietary programs.  If your program is a subroutine library, you may\r
+consider it more useful to permit linking proprietary applications with the\r
+library.  If this is what you want to do, use the GNU Library General\r
+Public License instead of this License.\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt
new file mode 100644 (file)
index 0000000..9ec576e
--- /dev/null
@@ -0,0 +1,53 @@
+File IO Lib Options\r
+-=-=-=-=-=-=-=-=-=-\r
+\r
+See defines in fat_opts.h:\r
+\r
+FATFS_IS_LITTLE_ENDIAN         [1/0]\r
+  Which endian is your system? Set to 1 for little endian, 0 for big endian.\r
+\r
+FATFS_MAX_LONG_FILENAME        [260]\r
+  By default, 260 characters (max LFN length). Increase this to support greater path depths.\r
+\r
+FATFS_MAX_OPEN_FILES   \r
+  The more files you wish to have concurrently open, the greater this number should be.\r
+  This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).\r
+\r
+FAT_BUFFER_SECTORS\r
+  Minimum is 1, more increases performance.\r
+  This defines how many FAT sectors can be buffered per FAT_BUFFER entry.\r
+\r
+FAT_BUFFERS\r
+  Minimum is 1, more increases performance.\r
+  This defines how many FAT buffer entries are available.\r
+  Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE\r
+\r
+FATFS_INC_WRITE_SUPPORT\r
+  Support file write functionality.\r
+\r
+FAT_SECTOR_SIZE\r
+  Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).\r
+\r
+FAT_PRINTF\r
+  A define that allows the File IO library to print to console/stdout. \r
+  Provide your own printf function if printf not available.\r
+\r
+FAT_CLUSTER_CACHE_ENTRIES\r
+  Size of cluster chain cache (can be undefined if not required).\r
+  Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2\r
+  Improves access speed considerably.\r
+\r
+FATFS_INC_LFN_SUPPORT  [1/0]\r
+  Enable/Disable support for long filenames.\r
+\r
+FATFS_DIR_LIST_SUPPORT         [1/0]\r
+  Include support for directory listing.\r
+\r
+FATFS_INC_TIME_DATE_SUPPORT  [1/0]\r
+  Use time/date functions provided by time.h to update creation & modification timestamps.\r
+\r
+FATFS_INC_FORMAT_SUPPORT\r
+  Include support for formatting disks (FAT16 only).\r
+\r
+FAT_PRINTF_NOINC_STDIO\r
+  Disable use of printf & inclusion of stdio.h\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt
new file mode 100644 (file)
index 0000000..58958f4
--- /dev/null
@@ -0,0 +1,24 @@
+Revision History\r
+-=-=-=-=-=-=-=-=-\r
+v2.6.11 - Fix compilation with GCC on 64-bit machines\r
+v2.6.10 - Added support for FAT32 format.\r
+V2.6.9  - Added support for time & date handling.\r
+V2.6.8  - Fixed error with FSINFO sector write.\r
+V2.6.7  - Added fgets().\r
+          Fixed C warnings, removed dependancy on some string.h functions.\r
+V2.6.6  \96 Massive read + write performance  improvements.\r
+V2.6.5  \96 Bug fixes for big endian systems.\r
+V2.6.4  \96 Further bug fixes and performance improvements for write operations.\r
+V2.6.3  \96 Peformance improvements, FAT16 formatting support. Various bug fixes.\r
+V2.6    - Basic support for FAT16 added (18-04-10).\r
+V2.5    - Code cleaned up. Many bugs fixed. Thread safety functions added.\r
+V2.x    - Write support added as well as better stdio like API.\r
+V1.0    - Rewrite of all code to enable multiple files to be opened and provides a \r
+          better file API.\r
+         Also better string matching, and generally better C code than origonal \r
+          version.\r
+V0.1c   - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping \r
+          the drive reads from addressing a sector that is out of range.\r
+V0.1b   - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 \r
+          access.\r
+V0.1a   - First release (27/12/03); fopen(), fgetc() unbuffered reads.\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt
new file mode 100644 (file)
index 0000000..c7fb0cc
--- /dev/null
@@ -0,0 +1,10 @@
+FAT File IO Library License\r
+-=-=-=-=-=-=-=-=-=-=-=-=-=-\r
+\r
+This versions license: GPL\r
+\r
+If you include GPL software in your project, you must release the source code of that project too.\r
+\r
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.\r
+\r
+Email: admin@ultra-embedded.com\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt
new file mode 100644 (file)
index 0000000..45eede0
--- /dev/null
@@ -0,0 +1,40 @@
+Media Access API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+       Sector: 32-bit sector number\r
+       Buffer: Target buffer to read n sectors of data into.\r
+       Sector_count: Number of sectors to read.\r
+\r
+Return: \r
+       int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+   Application/target specific disk/media read function.\r
+   Sector number (sectors are usually 512 byte pages) to read.\r
+\r
+Media Write API\r
+\r
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+       Sector: 32-bit sector number\r
+       Buffer: Target buffer to write n sectors of data from.\r
+       Sector_count: Number of sectors to write.\r
+\r
+Return: \r
+       int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+   Application/target specific disk/media write function.\r
+   Sector number (sectors are usually 512 byte pages) to write to.\r
+\r
+File IO Library Linkage\r
+   Use the following API to attach the media IO functions to the File IO library.\r
+\r
+   int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+\r
+\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c
new file mode 100644 (file)
index 0000000..5d30e5b
--- /dev/null
@@ -0,0 +1,87 @@
+#include <stdio.h>\r
+#include "fat_filelib.h"\r
+\r
+int media_init()\r
+{\r
+    // ...\r
+    return 1;\r
+}\r
+\r
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+    unsigned long i;\r
+\r
+    for (i=0;i<sector_count;i++)\r
+    {\r
+        // ...\r
+        // Add platform specific sector (512 bytes) read code here\r
+        //..\r
+\r
+        sector ++;\r
+        buffer += 512;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+    unsigned long i;\r
+\r
+    for (i=0;i<sector_count;i++)\r
+    {\r
+        // ...\r
+        // Add platform specific sector (512 bytes) write code here\r
+        //..\r
+\r
+        sector ++;\r
+        buffer += 512;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+void main()\r
+{\r
+    FL_FILE *file;\r
+\r
+    // Initialise media\r
+    media_init();\r
+\r
+    // Initialise File IO Library\r
+    fl_init();\r
+\r
+    // Attach media access functions to library\r
+    if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)\r
+    {\r
+        printf("ERROR: Media attach failed\n");\r
+        return; \r
+    }\r
+\r
+    // List root directory\r
+    fl_listdirectory("/");\r
+\r
+    // Create File\r
+    file = fl_fopen("/file.bin", "w");\r
+    if (file)\r
+    {\r
+        // Write some data\r
+        unsigned char data[] = { 1, 2, 3, 4 };\r
+        if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))\r
+            printf("ERROR: Write file failed\n");\r
+    }\r
+    else\r
+        printf("ERROR: Create file failed\n");\r
+\r
+    // Close file\r
+    fl_fclose(file);\r
+\r
+    // Delete File\r
+    if (fl_remove("/file.bin") < 0)\r
+        printf("ERROR: Delete file failed\n");\r
+\r
+    // List root directory\r
+    fl_listdirectory("/");\r
+\r
+    fl_shutdown();\r
+}\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.c
new file mode 100644 (file)
index 0000000..f55bd59
--- /dev/null
@@ -0,0 +1,904 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+    uint8 num_of_fats;
+    uint16 reserved_sectors;
+    uint32 FATSz;
+    uint32 root_dir_sectors;
+    uint32 total_sectors;
+    uint32 data_sectors;
+    uint32 count_of_clusters;
+    uint8 valid_partition = 0;
+
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have a read function (write function is optional)
+    if (!fs->disk_io.read_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // MBR: Sector 0 on the disk
+    // NOTE: Some removeable media does not have this.
+
+    // Load MBR (LBA 0) into the 512 byte buffer
+    if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make Sure 0x55 and 0xAA are at end of sector
+    // (this should be the case regardless of the MBR or boot sector)
+    if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Now check again using the access function to prove endian conversion function
+    if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+        return FAT_INIT_ENDIAN_ERROR;
+
+    // Verify packed structures
+    if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+        return FAT_INIT_STRUCT_PACKING;
+
+    // Check the partition type code
+    switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+    {
+        case 0x0B:
+        case 0x06:
+        case 0x0C:
+        case 0x0E:
+        case 0x0F:
+        case 0x05:
+            valid_partition = 1;
+        break;
+        case 0x00:
+            valid_partition = 0;
+            break;
+        default:
+            if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+                valid_partition = 1;
+        break;
+    }
+
+    // Read LBA Begin for the file system
+    if (valid_partition)
+        fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+    // Else possibly MBR less disk
+    else
+        fs->lba_begin = 0;\r
+\r
+    // Load Volume 1 table into sector buffer
+    // (We may already have this in the buffer if MBR less drive!)
+    if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make sure there are 512 bytes per cluster
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+        return FAT_INIT_INVALID_SECTOR_SIZE;\r
+
+    // Load Parameters of FAT partition
+    fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+    reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+    num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+    fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    // For FAT32 (which this may be)
+    fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+    fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Calculate the root dir sectors
+    root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+        total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+    else
+        total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+    data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+    // Find out which version of FAT this is...
+    if (fs->sectors_per_cluster != 0)
+    {
+        count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+        if(count_of_clusters < 4085)
+            // Volume is FAT12
+            return FAT_INIT_WRONG_FILESYS_TYPE;
+        else if(count_of_clusters < 65525)
+        {
+            // Clear this FAT32 specific param
+            fs->rootdir_first_cluster = 0;
+
+            // Volume is FAT16
+            fs->fat_type = FAT_TYPE_16;
+            return FAT_INIT_OK;
+        }
+        else
+        {
+            // Volume is FAT32
+            fs->fat_type = FAT_TYPE_32;
+            return FAT_INIT_OK;
+        }
+    }
+    else
+        return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+    else
+        return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+    uint32 sector_to_read = 0;
+    uint32 cluster_to_read = 0;
+    uint32 cluster_chain = 0;
+    uint32 i;
+    uint32 lba;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+    {
+        if (offset < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+        else
+            return 0;
+    }
+    // FAT16/32 Other
+    else
+    {
+        // Set start of cluster chain to initial value
+        cluster_chain = start_cluster;
+
+        // Find parameters
+        cluster_to_read = offset / fs->sectors_per_cluster;
+        sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+        // Follow chain to find cluster to read
+        for (i=0; i<cluster_to_read; i++)
+            cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+        // If end of cluster chain then return false
+        if (cluster_chain == FAT32_LAST_CLUSTER)
+            return 0;
+
+        // Calculate sector address
+        lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+    }
+
+    // User provided target array
+    if (target)
+        return fs->disk_io.read_media(lba, target, 1);
+    // Else read sector if not already loaded
+    else if (lba != fs->currentsector.address)
+    {
+        fs->currentsector.address = lba;
+        return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+    }
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16, there are a limited amount of sectors in root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate read address
+            fs->currentsector.address = lba;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate read address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16 we cannot extend the root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = lba;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate write address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+    FAT_PRINTF(("FAT details:\r\n"));
+    FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+    FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+    FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+    FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+    FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+    // NOTE: On FAT16 this will be 0 which has a special meaning...
+    return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    struct fat_dir_entry *directoryEntry;
+
+    fatfs_lfn_cache_init(&lfn, 1);
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(long_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(short_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                        return 1;
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+    time_t time_now;
+    struct tm * time_info;
+    uint16 fat_time;
+    uint16 fat_date;
+
+    // Get system time
+    time(&time_now);
+
+    // Convert to local time
+    time_info = localtime(&time_now);
+
+    // Convert time to FAT format
+    fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+    // Convert date to FAT format
+    fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+    // Update requested fields
+    if (create)
+    {
+        directoryEntry->CrtTime[1] = fat_time >> 8;
+        directoryEntry->CrtTime[0] = fat_time >> 0;
+        directoryEntry->CrtDate[1] = fat_date >> 8;
+        directoryEntry->CrtDate[0] = fat_date >> 0;
+    }
+
+    if (modify)
+    {
+        directoryEntry->WrtTime[1] = fat_time >> 8;
+        directoryEntry->WrtTime[0] = fat_time >> 0;
+        directoryEntry->WrtDate[1] = fat_date >> 8;
+        directoryEntry->WrtDate[0] = fat_date >> 0;
+    }
+
+    if (access)
+    {
+        directoryEntry->LstAccDate[1] = fat_time >> 8;
+        directoryEntry->LstAccDate[0] = fat_time >> 0;
+        directoryEntry->LstAccDate[1] = fat_date >> 8;
+        directoryEntry->LstAccDate[0] = fat_date >> 0;
+    }
+
+    return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        // Mark as deleted
+                        directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+    dirls->cluster = StartCluster;
+    dirls->sector = 0;
+    dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+    uint8 i,item;
+    uint16 recordoffset;
+    struct fat_dir_entry *directoryEntry;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    int result = 0;
+
+    // Initialise LFN cache first
+    fatfs_lfn_cache_init(&lfn, 0);
+
+    while (1)
+    {
+        // If data read OK
+        if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+        {
+            // Maximum of 16 directory entries
+            for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Increase directory offset
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if ( fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    // Get text
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+                    strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME-1);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if ( fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+            }// end of for
+
+            // If reached end of the dir move onto next sector
+            dirls->sector++;
+            dirls->offset = 0;
+        }
+        else
+            break;
+    }
+
+    return result;
+}
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c
new file mode 100644 (file)
index 0000000..de77e6a
--- /dev/null
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    int i;
+
+    for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+    {
+        file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+        file->cluster_cache_data[i] = 0;
+    }
+#endif
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+    {
+        *pNextCluster = file->cluster_cache_data[slot];
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+        file->cluster_cache_data[slot] = nextCluster;
+    else
+    {
+        file->cluster_cache_idx[slot] = clusterIdx;
+        file->cluster_cache_data[slot] = nextCluster;
+    }
+#endif
+
+    return 1;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c
new file mode 100644 (file)
index 0000000..2c4a236
--- /dev/null
@@ -0,0 +1,1603 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE            _files[FATFS_MAX_OPEN_FILES];
+static int                _filelib_init = 0;
+static int                _filelib_valid = 0;
+static struct fatfs       _fs;
+static struct fat_list    _open_file_list;
+static struct fat_list    _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT()     { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a)          do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a)        do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void                _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+    // Allocate free file
+    struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+    // Add to open list
+    if (node)
+        fat_list_insert_last(&_open_file_list, node);
+
+    return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+    struct fat_node *node;
+
+    // Compare open files
+    fat_list_for_each(&_open_file_list, node)
+    {
+        FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+        // If not the current file
+        if (openFile != file)
+        {
+            // Compare path and name
+            if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+                return 1;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+    // Remove from open list
+    fat_list_remove(&_open_file_list, &file->list_node);
+
+    // Add to free list
+    fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+//                                Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+    int levels;
+    int sublevel;
+    char currentfolder[FATFS_MAX_LONG_FILENAME];
+    struct fat_dir_entry sfEntry;
+    uint32 startcluster;
+
+    // Set starting cluster to root cluster
+    startcluster = fatfs_get_root_cluster(&_fs);
+
+    // Find number of levels
+    levels = fatfs_total_path_levels(path);
+
+    // Cycle through each level and get the start sector
+    for (sublevel=0;sublevel<(levels+1);sublevel++)
+    {
+        if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+            return 0;
+
+        // Find clusteraddress for folder (currentfolder)
+        if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+        {
+            // Check entry is folder
+            if (fatfs_entry_is_dir(&sfEntry))
+                startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            else
+                return 0;
+        }
+        else
+            return 0;
+    }
+
+    *pathCluster = startcluster;
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+    int i;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return 0;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the folder (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Erase new directory cluster
+    memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+    for (i=0;i<_fs.sectors_per_cluster;i++)
+    {
+        if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_fat_purge(&_fs);
+
+    _free_file(file);
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0]==0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Using dir cluster address search for filename
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+        // Make sure entry is file not dir!
+        if (fatfs_entry_is_file(&sfEntry))
+        {
+            // Initialise file details
+            memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+            file->filelength = FAT_HTONL(sfEntry.FileSize);
+            file->bytenum = 0;
+            file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            file->file_data_address = 0xFFFFFFFF;
+            file->file_data_dirty = 0;
+            file->filelength_changed = 0;
+
+            // Quick lookup for next link in the chain
+            file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+            file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+            fatfs_cache_init(&_fs, file);
+
+            fatfs_fat_purge(&_fs);
+
+            return file;
+        }
+
+    _free_file(file);
+    return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+
+    // No write access?
+    if (!_fs.disk_io.write_media)
+        return NULL;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the file (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_cache_init(&_fs, file);
+
+    fatfs_fat_purge(&_fs);
+
+    return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+    uint32 Sector = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 i;
+    uint32 lba;
+
+    // Find cluster index within file & sector with cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors read to the number remaining in this cluster
+    if ((Sector + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - Sector;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            Cluster = nextCluster;
+        }
+
+        // Record current cluster lookup details (if valid)
+        if (Cluster != FAT32_LAST_CLUSTER)
+        {
+            file->last_fat_lookup.CurrentCluster = Cluster;
+            file->last_fat_lookup.ClusterIdx = ClusterIdx;
+        }
+    }
+
+    // If end of cluster chain then return false
+    if (Cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Calculate sector address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+    // Read sector of file
+    if (fatfs_sector_read(&_fs, lba, buffer, count))
+        return count;
+    else
+        return 0;
+}
+
+//-----------------------------------------------------------------------------
+//                                External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+    int i;
+
+    fat_list_init(&_free_file_list);
+    fat_list_init(&_open_file_list);
+
+    // Add all file objects to free list
+    for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+        fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+    _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+    _fs.fl_lock = lock;
+    _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    _fs.disk_io.read_media = rd;
+    _fs.disk_io.write_media = wr;
+
+    // Initialise FAT parameters
+    if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+    {
+        FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+        return res;
+    }
+
+    _filelib_valid = 1;
+    return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    fatfs_fat_purge(&_fs);
+    FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+    int i;
+    FL_FILE* file;
+    uint8 flags = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!_filelib_valid)
+        return NULL;
+
+    if (!path || !mode)
+        return NULL;
+
+    // Supported Modes:
+    // "r" Open a file for reading.
+    //        The file must exist.
+    // "w" Create an empty file for writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a" Append to a file.
+    //        Writing operations append data at the end of the file.
+    //        The file is created if it does not exist.
+    // "r+" Open a file for update both reading and writing.
+    //        The file must exist.
+    // "w+" Create an empty file for both reading and writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a+" Open a file for reading and appending.
+    //        All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+    //        You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+    //        will move it back to the end of file.
+    //        The file is created if it does not exist.
+
+    for (i=0;i<(int)strlen(mode);i++)
+    {
+        switch (mode[i])
+        {
+        case 'r':
+        case 'R':
+            flags |= FILE_READ;
+            break;
+        case 'w':
+        case 'W':
+            flags |= FILE_WRITE;
+            flags |= FILE_ERASE;
+            flags |= FILE_CREATE;
+            break;
+        case 'a':
+        case 'A':
+            flags |= FILE_WRITE;
+            flags |= FILE_APPEND;
+            flags |= FILE_CREATE;
+            break;
+        case '+':
+            if (flags & FILE_READ)
+                flags |= FILE_WRITE;
+            else if (flags & FILE_WRITE)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_ERASE;
+                flags |= FILE_CREATE;
+            }
+            else if (flags & FILE_APPEND)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_WRITE;
+                flags |= FILE_APPEND;
+                flags |= FILE_CREATE;
+            }
+            break;
+        case 'b':
+        case 'B':
+            flags |= FILE_BINARY;
+            break;
+        }
+    }
+
+    file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+    // No write support!
+    flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+    // No write access - remove write/modify flags
+    if (!_fs.disk_io.write_media)
+        flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+    FL_LOCK(&_fs);
+
+    // Read
+    if (flags & FILE_READ)
+        file = _open_file(path);
+
+    // Create New
+#if FATFS_INC_WRITE_SUPPORT
+    if (!file && (flags & FILE_CREATE))
+        file = _create_file(path);
+#endif
+
+    // Write Existing (and not open due to read or create)
+    if (!(flags & FILE_READ))
+        if ((flags & FILE_CREATE) && !file)
+            if (flags & (FILE_WRITE | FILE_APPEND))
+                file = _open_file(path);
+
+    if (file)
+        file->flags = flags;
+
+    FL_UNLOCK(&_fs);
+    return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+    uint32 SectorNumber = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 LastCluster = FAT32_LAST_CLUSTER;
+    uint32 i;
+    uint32 lba;
+    uint32 TotalWriteCount = count;
+
+    // Find values for Cluster index & sector within cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors written to the number remaining in this cluster
+    if ((SectorNumber + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - SectorNumber;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            LastCluster = Cluster;
+            Cluster = nextCluster;
+
+            // Dont keep following a dead end
+            if (Cluster == FAT32_LAST_CLUSTER)
+                break;
+        }
+
+        // If we have reached the end of the chain, allocate more!
+        if (Cluster == FAT32_LAST_CLUSTER)
+        {
+            // Add some more cluster(s) to the last good cluster chain
+            if (!fatfs_add_free_space(&_fs, &LastCluster,  (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+                return 0;
+
+            Cluster = LastCluster;
+        }
+
+        // Record current cluster lookup details
+        file->last_fat_lookup.CurrentCluster = Cluster;
+        file->last_fat_lookup.ClusterIdx = ClusterIdx;
+    }
+
+    // Calculate write address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+    if (fatfs_sector_write(&_fs, lba, buf, count))
+        return count;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // If some write data still in buffer
+        if (file->file_data_dirty)
+        {
+            // Write back current sector before loading next
+            if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+                file->file_data_dirty = 0;
+        }
+
+        FL_UNLOCK(&_fs);
+    }
+#endif
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // Flush un-written data to file
+        fl_fflush(f);
+
+        // File size changed?
+        if (file->filelength_changed)
+        {
+#if FATFS_INC_WRITE_SUPPORT
+            // Update filesize in directory
+            fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+            file->filelength_changed = 0;
+        }
+
+        file->bytenum = 0;
+        file->filelength = 0;
+        file->startcluster = 0;
+        file->file_data_address = 0xFFFFFFFF;
+        file->file_data_dirty = 0;
+        file->filelength_changed = 0;
+
+        // Free file handle
+        _free_file(file);
+
+        fatfs_fat_purge(&_fs);
+
+        FL_UNLOCK(&_fs);
+    }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+    int res;
+    uint8 data = 0;
+
+    res = fl_fread(&data, 1, 1, f);
+    if (res == 1)
+        return (int)data;
+    else
+        return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+    int idx = 0;
+
+    // Space for null terminator?
+    if (n > 0)
+    {
+        // While space (+space for null terminator)
+        while (idx < (n-1))
+        {
+            int ch = fl_fgetc(f);
+
+            // EOF / Error?
+            if (ch < 0)
+                break;
+
+            // Store character read from stream
+            s[idx++] = (char)ch;
+
+            // End of line?
+            if (ch == '\n')
+                break;
+        }
+
+        if (idx > 0)
+            s[idx] = '\0';
+    }
+
+    return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+    uint32 sector;
+    uint32 offset;
+    int copyCount;
+    int count = size * length;
+    int bytesRead = 0;
+
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (buffer==NULL || file==NULL)
+        return -1;
+
+    // No read permissions
+    if (!(file->flags & FILE_READ))
+        return -1;
+
+    // Nothing to be done
+    if (!count)
+        return 0;
+
+    // Check if read starts past end of file
+    if (file->bytenum >= file->filelength)
+        return -1;
+
+    // Limit to file size
+    if ( (file->bytenum + count) > file->filelength )
+        count = file->filelength - file->bytenum;
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesRead < count)
+    {
+        // Read whole sector, read from media directly into target buffer
+        if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+        {
+            // Read as many sectors as possible into target buffer
+            uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+            if (sectorsRead)
+            {
+                // We have upto one sector to copy
+                copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+                // Move onto next sector and reset copy offset
+                sector+= sectorsRead;
+                offset = 0;
+            }
+            else
+                break;
+        }
+        else
+        {
+            // Do we need to re-read the sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // Get LBA of sector offset within file
+                if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                    // Read failed - out of range (probably)
+                    break;
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (count - bytesRead))
+                copyCount = (count - bytesRead);
+
+            // Copy to application buffer
+            memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+
+        // Increase total read count
+        bytesRead += copyCount;
+
+        // Increment file pointer
+        file->bytenum += copyCount;
+    }
+
+    return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res = -1;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    if (origin == SEEK_END && offset != 0)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Invalidate file buffer
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+
+    if (origin == SEEK_SET)
+    {
+        file->bytenum = (uint32)offset;
+
+        if (file->bytenum > file->filelength)
+            file->bytenum = file->filelength;
+
+        res = 0;
+    }
+    else if (origin == SEEK_CUR)
+    {
+        // Positive shift
+        if (offset >= 0)
+        {
+            file->bytenum += offset;
+
+            if (file->bytenum > file->filelength)
+                file->bytenum = file->filelength;
+        }
+        // Negative shift
+        else
+        {
+            // Make shift positive
+            offset = -offset;
+
+            // Limit to negative shift to start of file
+            if ((uint32)offset > file->bytenum)
+                file->bytenum = 0;
+            else
+                file->bytenum-= offset;
+        }
+
+        res = 0;
+    }
+    else if (origin == SEEK_END)
+    {
+        file->bytenum = file->filelength;
+        res = 0;
+    }
+    else
+        res = -1;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Get position
+    *position = file->bytenum;
+
+    FL_UNLOCK(&_fs);
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+    uint32 pos = 0;
+
+    fl_fgetpos(f, &pos);
+
+    return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    if (file->bytenum == file->filelength)
+        res = EOF;
+    else
+        res = 0;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+    uint8 data = (uint8)c;
+    int res;
+
+    res = fl_fwrite(&data, 1, 1, f);
+    if (res == 1)
+        return c;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    uint32 sector;
+    uint32 offset;
+    uint32 length = (size*count);
+    uint8 *buffer = (uint8 *)data;
+    uint32 bytesWritten = 0;
+    uint32 copyCount;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // No write permissions
+    if (!(file->flags & FILE_WRITE))
+    {
+        FL_UNLOCK(&_fs);
+        return -1;
+    }
+
+    // Append writes to end of file
+    if (file->flags & FILE_APPEND)
+        file->bytenum = file->filelength;
+    // Else write to current position
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesWritten < length)
+    {
+        // Whole sector or more to be written?
+        if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+        {
+            uint32 sectorsWrote;
+
+            // Buffered sector, flush back to disk
+            if (file->file_data_address != 0xFFFFFFFF)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                file->file_data_address = 0xFFFFFFFF;
+                file->file_data_dirty = 0;
+            }
+
+            // Write as many sectors as possible
+            sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+            copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector+= sectorsWrote;
+            offset = 0;
+
+            if (!sectorsWrote)
+                break;
+        }
+        else
+        {
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (length - bytesWritten))
+                copyCount = (length - bytesWritten);
+
+            // Do we need to read a new sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // If we plan to overwrite the whole sector, we don't need to read it first!
+                if (copyCount != FAT_SECTOR_SIZE)
+                {
+                    // NOTE: This does not have succeed; if last sector of file
+                    // reached, no valid data will be read in, but write will
+                    // allocate some more space for new data.
+
+                    // Get LBA of sector offset within file
+                    if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                        memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+                }
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // Copy from application buffer into sector buffer
+            memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+            // Mark buffer as dirty
+            file->file_data_dirty = 1;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+    }
+
+    // Write increased extent of the file?
+    if (file->bytenum > file->filelength)
+    {
+        // Increase file size to new point
+        file->filelength = file->bytenum;
+
+        // We are changing the file length and this
+        // will need to be writen back at some point
+        file->filelength_changed = 1;
+    }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    // If time & date support is enabled, always force directory entry to be
+    // written in-order to update file modify / access time & date.
+    file->filelength_changed = 1;
+#endif
+
+    FL_UNLOCK(&_fs);
+
+    return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+    int len = (int)strlen(str);
+    int res = fl_fwrite(str, 1, len, f);
+
+    if (res == len)
+        return len;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+    FL_FILE* file;
+    int res = -1;
+
+    FL_LOCK(&_fs);
+
+    // Use read_file as this will check if the file is already open!
+    file = fl_fopen((char*)filename, "r");
+    if (file)
+    {
+        // Delete allocated space
+        if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+        {
+            // Remove directory entries
+            if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+            {
+                // Close the file handle (this should not write anything to the file
+                // as we have not changed the file since opening it!)
+                fl_fclose(file);
+
+                res = 0;
+            }
+        }
+    }
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    res =_create_directory((char*)path);
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+    FL_DIR dirstat;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+    if (fl_opendir(path, &dirstat))
+    {
+        struct fs_dir_ent dirent;
+
+        while (fl_readdir(&dirstat, &dirent) == 0)
+        {
+#if FATFS_INC_TIME_DATE_SUPPORT
+            int d,m,y,h,mn,s;
+            fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+            fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+            FAT_PRINTF(("%02d/%02d/%04d  %02d:%02d      ", d,mn,y,h,m));
+#endif
+
+            if (dirent.is_dir)
+            {
+                FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+            }
+            else
+            {
+                FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+            }
+        }
+
+        fl_closedir(&dirstat);
+    }
+
+    FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+    int levels;
+    int res = 1;
+    uint32 cluster = FAT32_INVALID_CLUSTER;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    levels = fatfs_total_path_levels((char*)path) + 1;
+
+    // If path is in the root dir
+    if (levels == 0)
+        cluster = fatfs_get_root_cluster(&_fs);
+    // Find parent directory start cluster
+    else
+        res = _open_directory((char*)path, &cluster);
+
+    if (res)
+        fatfs_list_directory_start(&_fs, dir, cluster);
+
+    FL_UNLOCK(&_fs);
+
+    return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+    int res = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+    FL_UNLOCK(&_fs);
+
+    return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+    // Not used
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+    int res = 0;
+    FL_DIR dir;
+
+    if (fl_opendir(path, &dir))
+    {
+        res = 1;
+        fl_closedir(&dir);
+    }
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+    return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+    return &_fs;
+}
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h
new file mode 100644 (file)
index 0000000..a40a28f
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#define FILE_CREATE         (1 << 5)
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c
new file mode 100644 (file)
index 0000000..d067f37
--- /dev/null
@@ -0,0 +1,532 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+    uint32  sectors;
+    uint8   sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+    { 32680, 2},    // 16MB - 1K
+    { 262144, 4},   // 128MB - 2K
+    { 524288, 8},   // 256MB - 4K
+    { 1048576, 16}, // 512MB - 8K
+    { 2097152, 32}, // 1GB - 16K
+    { 4194304, 64}, // 2GB - 32K
+    { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+    { 0 , 0 }       // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+    { 532480, 1},     // 260MB - 512b
+    { 16777216, 8},   // 8GB - 4K
+    { 33554432, 16},  // 16GB - 8K
+    { 67108864, 32},  // 32GB - 16K
+    { 0xFFFFFFFF, 64},// >32GB - 32K
+    { 0 , 0 }         // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+    int i;
+
+    if (!is_fat32)
+    {
+        for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table16[i].sectors)
+                return _cluster_size_table16[i].sectors_per_cluster;
+    }
+    else
+    {
+        for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table32[i].sectors)
+                return _cluster_size_table32[i].sectors_per_cluster;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+    int i;
+
+    // Zero sector first
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    for (i=0;i<count;i++)
+        if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+    uint32 total_clusters;
+    int i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // OEM Name & Jump Code
+    fs->currentsector.sector[0] = 0xEB;
+    fs->currentsector.sector[1] = 0x3C;
+    fs->currentsector.sector[2] = 0x90;
+    fs->currentsector.sector[3] = 0x4D;
+    fs->currentsector.sector[4] = 0x53;
+    fs->currentsector.sector[5] = 0x44;
+    fs->currentsector.sector[6] = 0x4F;
+    fs->currentsector.sector[7] = 0x53;
+    fs->currentsector.sector[8] = 0x35;
+    fs->currentsector.sector[9] = 0x2E;
+    fs->currentsector.sector[10] = 0x30;
+
+    // Bytes per sector
+    fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+    fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+    // Get sectors per cluster size for the disk
+    fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+    if (!fs->sectors_per_cluster)
+        return 0; // Invalid disk size
+
+    // Sectors per cluster
+    fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+    // Reserved Sectors
+    if (!is_fat32)
+        fs->reserved_sectors = 8;
+    else
+        fs->reserved_sectors = 32;
+    fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+    fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+    // Number of FATS
+    fs->num_of_fats = 2;
+    fs->currentsector.sector[16] = fs->num_of_fats;
+
+    // Max entries in root dir (FAT16 only)
+    if (!is_fat32)
+    {
+        fs->root_entry_count = 512;
+        fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+        fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+    }
+    else
+    {
+        fs->root_entry_count = 0;
+        fs->currentsector.sector[17] = 0;
+        fs->currentsector.sector[18] = 0;
+    }
+
+    // [FAT16] Total sectors (use FAT32 count instead)
+    fs->currentsector.sector[19] = 0x00;
+    fs->currentsector.sector[20] = 0x00;
+
+    // Media type
+    fs->currentsector.sector[21] = 0xF8;
+
+
+    // FAT16 BS Details
+    if (!is_fat32)
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+        fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+        fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+        // Sectors per track
+        fs->currentsector.sector[24] = 0x00;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads
+        fs->currentsector.sector[26] = 0x00;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x20;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        // Drive number
+        fs->currentsector.sector[36] = 0x00;
+
+        // Reserved
+        fs->currentsector.sector[37] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[38] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[39] = 0x12;
+        fs->currentsector.sector[40] = 0x34;
+        fs->currentsector.sector[41] = 0x56;
+        fs->currentsector.sector[42] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+43] = name[i];
+            else
+                fs->currentsector.sector[i+43] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[54] = 'F';
+        fs->currentsector.sector[55] = 'A';
+        fs->currentsector.sector[56] = 'T';
+        fs->currentsector.sector[57] = '1';
+        fs->currentsector.sector[58] = '6';
+        fs->currentsector.sector[59] = ' ';
+        fs->currentsector.sector[60] = ' ';
+        fs->currentsector.sector[61] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+    // FAT32 BS Details
+    else
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        fs->currentsector.sector[22] = 0;
+        fs->currentsector.sector[23] = 0;
+
+        // Sectors per track (default)
+        fs->currentsector.sector[24] = 0x3F;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads (default)
+        fs->currentsector.sector[26] = 0xFF;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x00;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+        // BPB_FATSz32
+        fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+        fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+        fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+        fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+        // BPB_ExtFlags
+        fs->currentsector.sector[40] = 0;
+        fs->currentsector.sector[41] = 0;
+
+        // BPB_FSVer
+        fs->currentsector.sector[42] = 0;
+        fs->currentsector.sector[43] = 0;
+
+        // BPB_RootClus
+        fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+        fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+        fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+        fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+        // BPB_FSInfo
+        fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+        fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+        // BPB_BkBootSec
+        fs->currentsector.sector[50] = 6;
+        fs->currentsector.sector[51] = 0;
+
+        // Drive number
+        fs->currentsector.sector[64] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[66] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[67] = 0x12;
+        fs->currentsector.sector[68] = 0x34;
+        fs->currentsector.sector[69] = 0x56;
+        fs->currentsector.sector[70] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+71] = name[i];
+            else
+                fs->currentsector.sector[i+71] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[82] = 'F';
+        fs->currentsector.sector[83] = 'A';
+        fs->currentsector.sector[84] = 'T';
+        fs->currentsector.sector[85] = '3';
+        fs->currentsector.sector[86] = '2';
+        fs->currentsector.sector[87] = ' ';
+        fs->currentsector.sector[88] = ' ';
+        fs->currentsector.sector[89] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+
+    if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // FSI_LeadSig
+    fs->currentsector.sector[0] = 0x52;
+    fs->currentsector.sector[1] = 0x52;
+    fs->currentsector.sector[2] = 0x61;
+    fs->currentsector.sector[3] = 0x41;
+
+    // FSI_StrucSig
+    fs->currentsector.sector[484] = 0x72;
+    fs->currentsector.sector[485] = 0x72;
+    fs->currentsector.sector[486] = 0x41;
+    fs->currentsector.sector[487] = 0x61;
+
+    // FSI_Free_Count
+    fs->currentsector.sector[488] = 0xFF;
+    fs->currentsector.sector[489] = 0xFF;
+    fs->currentsector.sector[490] = 0xFF;
+    fs->currentsector.sector[491] = 0xFF;
+
+    // FSI_Nxt_Free
+    fs->currentsector.sector[492] = 0xFF;
+    fs->currentsector.sector[493] = 0xFF;
+    fs->currentsector.sector[494] = 0xFF;
+    fs->currentsector.sector[495] = 0xFF;
+
+    // Signature
+    fs->currentsector.sector[510] = 0x55;
+    fs->currentsector.sector[511] = 0xAA;
+
+    if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+    uint32 i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // Initialise default allocate / reserved clusters
+    if (!is_fat32)
+    {
+        SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+        SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+    }
+    else
+    {
+        SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+        SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+        SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+    }
+
+    if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+        return 0;
+
+    // Zero remaining FAT sectors
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+    for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+        if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT16
+    fs->fat_type = FAT_TYPE_16;
+
+    // Not valid for FAT16
+    fs->fs_info_sector = 0;
+    fs->rootdir_first_cluster = 0;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+        return 0;
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 0))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT32
+    fs->fat_type = FAT_TYPE_32;
+
+    // Basic defaults for normal FAT32 partitions
+    fs->fs_info_sector = 1;
+    fs->rootdir_first_cluster = 2;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+        return 0;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FSInfo sector
+    if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+        return 0;
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 1))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    // 2GB - 32K limit for safe behaviour for FAT16
+    if (volume_sectors <= 4194304)
+        return fatfs_format_fat16(fs, volume_sectors, name);
+    else
+        return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c
new file mode 100644 (file)
index 0000000..cbf6f08
--- /dev/null
@@ -0,0 +1,505 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+    int i = 0;
+
+    lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Zero out buffer also
+    if (wipeTable)
+        for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+            memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+    uint8 LFNIndex, i;
+    LFNIndex = entryBuffer[0] & 0x1F;
+
+    // Limit file name to cache size!
+    if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+        return ;
+
+    // This is an error condition
+    if (LFNIndex == 0)
+        return ;
+
+    if (lfn->no_of_strings == 0)
+        lfn->no_of_strings = LFNIndex;
+
+    lfn->String[LFNIndex-1][0] = entryBuffer[1];
+    lfn->String[LFNIndex-1][1] = entryBuffer[3];
+    lfn->String[LFNIndex-1][2] = entryBuffer[5];
+    lfn->String[LFNIndex-1][3] = entryBuffer[7];
+    lfn->String[LFNIndex-1][4] = entryBuffer[9];
+    lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+    lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+    lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+    lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+    lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+    lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+    lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+    lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+    for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+        if (lfn->String[LFNIndex-1][i]==0xFF)
+            lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+    // Null terminate long filename
+    if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+        lfn->Null = '\0';
+    else if (lfn->no_of_strings)
+        lfn->String[lfn->no_of_strings][0] = '\0';
+    else
+        lfn->String[0][0] = '\0';
+
+    return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+    if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+    if ( (entry->Name[0]==FILE_HEADER_BLANK)  ||
+         (entry->Name[0]==FILE_HEADER_DELETED)||
+         (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+         (entry->Attr & FILE_ATTR_SYSHID) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+         (lfn->no_of_strings) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) )
+        return 1;
+    else
+        return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_DIR)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_FILE)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+    int length = (int)strlen(filename);
+
+    if (length)
+        return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+    int i;
+    int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+    // 13 characters entries
+    int length = (int)strlen(filename);
+    int entriesRequired = fatfs_lfn_entries_required(filename);
+
+    // Filename offset
+    int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+    // Initialise to zeros
+    memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+    // LFN entry number
+    buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+    // LFN flag
+    buffer[11] = 0x0F;
+
+    // Checksum of short filename
+    buffer[13] = sfnChk;
+
+    // Copy to buffer
+    for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+    {
+        if ( (start+i) < length )
+            buffer[nameIndexes[i]] = filename[start+i];
+        else if ( (start+i) == length )
+            buffer[nameIndexes[i]] = 0x00;
+        else
+        {
+            buffer[nameIndexes[i]] = 0xFF;
+            buffer[nameIndexes[i]+1] = 0xFF;
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+    int i;
+
+    // Copy short filename
+    for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+        entry->Name[i] = shortfilename[i];
+
+    // Unless we have a RTC we might as well set these to 1980
+    entry->CrtTimeTenth = 0x00;
+    entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+    entry->CrtDate[1] = 0x00;
+    entry->CrtDate[0] = 0x20;
+    entry->LstAccDate[1] = 0x00;
+    entry->LstAccDate[0] = 0x20;
+    entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+    entry->WrtDate[1] = 0x00;
+    entry->WrtDate[0] = 0x20;
+
+    if (!dir)
+        entry->Attr = FILE_TYPE_FILE;
+    else
+        entry->Attr = FILE_TYPE_DIR;
+
+    entry->NTRes = 0x00;
+
+    entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+    entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+    entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+    int i;
+    int dotPos = -1;
+    char ext[3];
+    int pos;
+    int len = (int)strlen(filename);
+
+    // Invalid to start with .
+    if (filename[0]=='.')
+        return 0;
+
+    memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+    memset(ext, ' ', 3);
+
+    // Find dot seperator
+    for (i = 0; i< len; i++)
+    {
+        if (filename[i]=='.')
+            dotPos = i;
+    }
+
+    // Extract extensions
+    if (dotPos!=-1)
+    {
+        // Copy first three chars of extension
+        for (i = (dotPos+1); i < (dotPos+1+3); i++)
+            if (i<len)
+                ext[i-(dotPos+1)] = filename[i];
+
+        // Shorten the length to the dot position
+        len = dotPos;
+    }
+
+    // Add filename part
+    pos = 0;
+    for (i=0;i<len;i++)
+    {
+        if ( (filename[i]!=' ') && (filename[i]!='.') )
+        {
+            if (filename[i] >= 'a' && filename[i] <= 'z')
+                sfn_output[pos++] = filename[i] - 'a' + 'A';
+            else
+                sfn_output[pos++] = filename[i];
+        }
+
+        // Fill upto 8 characters
+        if (pos==FAT_SFN_SIZE_PARTIAL)
+            break;
+    }
+
+    // Add extension part
+    for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+    {
+        if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+        else
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+    char* cp;
+    char outbuf[12];
+    const char digits[] = "0123456789ABCDEF";
+
+    // Build string backwards
+    cp = outbuf;
+    do
+    {
+        *cp++ = digits[(int)(num % 10)];
+    }
+    while ((num /= 10) > 0);
+
+    *cp-- = 0;
+
+    // Copy in forwards
+    while (cp >= outbuf)
+        *s++ = *cp--;
+
+    *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+    int tail_chars;
+    char tail_str[12];
+
+    if (tailNum > 99999)
+        return 0;
+
+    // Convert to number
+    memset(tail_str, 0x00, sizeof(tail_str));
+    tail_str[0] = '~';
+    fatfs_itoa(tailNum, tail_str+1);
+
+    // Copy in base filename
+    memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+    // Overwrite with tail
+    tail_chars = (int)strlen(tail_str);
+    memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+    return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+    *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+    *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+    *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+    *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+    *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+    *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+    *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+    *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+    uint16 fat_time = 0;
+
+    // Most FAT times are to a resolution of 2 seconds
+    seconds /= FAT_TIME_SECONDS_SCALE;
+
+    fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+    fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+    fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+    return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+    uint16 fat_date = 0;
+
+    // FAT dates are relative to 1980
+    if (year >= FAT_DATE_YEAR_OFFSET)
+        year -= FAT_DATE_YEAR_OFFSET;
+
+    fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+    fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+    fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+    return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+    int i;
+    int j;
+
+    FAT_PRINTF(("Sector %d:\n", sector));
+
+    for (i=0;i<FAT_SECTOR_SIZE;i++)
+    {
+        if (!((i) % 16))
+        {
+            FAT_PRINTF(("  %04d: ", i));
+        }
+
+        FAT_PRINTF(("%02x", data[i]));
+        if (!((i+1) % 4))
+        {
+            FAT_PRINTF((" "));
+        }
+
+        if (!((i+1) % 16))
+        {
+            FAT_PRINTF(("   "));
+            for (j=0;j<16;j++)
+            {
+                char ch = data[i-15+j];
+
+                // Is printable?
+                if (ch > 31 && ch < 127)
+                {
+                    FAT_PRINTF(("%c", ch));
+                }
+                else
+                {
+                    FAT_PRINTF(("."));
+                }
+            }
+
+            FAT_PRINTF(("\n"));
+        }
+    }
+}
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h
new file mode 100644 (file)
index 0000000..ac4dc86
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        #define FAT_PRINTF(a)               printf a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c
new file mode 100644 (file)
index 0000000..f7206ce
--- /dev/null
@@ -0,0 +1,514 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+//        c:\folder\file.zip
+//        /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+    int levels = 0;
+    char expectedchar;
+
+    if (!path)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Count levels in path string
+    while (*path)
+    {
+        // Fast forward through actual subdir text to next slash
+        for (; *path; )
+        {
+            // If slash detected escape from for loop
+            if (*path == expectedchar) { path++; break; }
+            path++;
+        }
+
+        // Increase number of subdirs founds
+        levels++;
+    }
+
+    // Subtract the file itself
+    return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+    int i;
+    int pathlen=0;
+    int levels=0;
+    int copypnt=0;
+    char expectedchar;
+
+    if (!path || max_len <= 0)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Get string length of path
+    pathlen = (int)strlen (path);
+
+    // Loop through the number of times as characters in 'path'
+    for (i = 0; i<pathlen; i++)
+    {
+        // If a '\' is found then increase level
+        if (*path == expectedchar) levels++;
+
+        // If correct level and the character is not a '\' or '/' then copy text to 'output'
+        if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+            output[copypnt++] = *path;
+
+        // Increment through path string
+        path++;
+    }
+
+    // Null Terminate
+    output[copypnt] = '\0';
+
+    // If a string was copied return 0 else return 1
+    if (output[0] != '\0')
+        return 0;    // OK
+    else
+        return -1;    // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder  filename = file.zip
+// E.g. C:\file.zip -> path = [blank]  filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+    int strindex;
+
+    // Count the levels to the filepath
+    int levels = fatfs_total_path_levels(full_path);
+    if (levels == -1)
+        return -1;
+
+    // Get filename part of string
+    if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+        return -1;
+
+    // If root file
+    if (levels == 0)
+        path[0] = '\0';
+    else
+    {
+        strindex = (int)strlen(full_path) - (int)strlen(filename);
+        if (strindex > max_path)
+            strindex = max_path;
+
+        memcpy(path, full_path, strindex);
+        path[strindex-1] = '\0';
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+    int diff;
+    char a,b;
+
+    while (n--)
+    {
+        a = *s1;
+        b = *s2;
+
+        // Make lower case if uppercase
+        if ((a>='A') && (a<='Z'))
+            a+= 32;
+        if ((b>='A') && (b<='Z'))
+            b+= 32;
+
+        diff = a - b;
+
+        // If different
+        if (diff)
+            return diff;
+
+        // If run out of strings
+        if ( (*s1 == 0) || (*s2 == 0) )
+            break;
+
+        s1++;
+        s2++;
+    }
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+    int dotPos = -1;
+    char *strSrc = str;
+
+    // Find last '.' in string (if at all)
+    while (*strSrc)
+    {
+        if (*strSrc=='.')
+            dotPos = (int)(strSrc-str);
+
+        strSrc++;
+    }
+
+    return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+    int length = strLen;
+    char *strSrc = str+strLen-1;
+
+    // Find last non white space
+    while (strLen != 0)
+    {
+        if (*strSrc == ' ')
+            length = (int)(strSrc - str);
+        else
+            break;
+
+        strSrc--;
+        strLen--;
+    }
+
+    return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+    char *ext1 = NULL;
+    char *ext2 = NULL;
+    int ext1Pos, ext2Pos;
+    int file1Len, file2Len;
+
+    // Get both files extension
+    ext1Pos = FileString_GetExtension(strA);
+    ext2Pos = FileString_GetExtension(strB);
+
+    // NOTE: Extension position can be different for matching
+    // filename if trailing space are present before it!
+    // Check that if one has an extension, so does the other
+    if ((ext1Pos==-1) && (ext2Pos!=-1))
+        return 0;
+    if ((ext2Pos==-1) && (ext1Pos!=-1))
+        return 0;
+
+    // If they both have extensions, compare them
+    if (ext1Pos!=-1)
+    {
+        // Set pointer to start of extension
+        ext1 = strA+ext1Pos+1;
+        ext2 = strB+ext2Pos+1;
+
+        // Verify that the file extension lengths match!
+        if (strlen(ext1) != strlen(ext2))
+            return 0;
+
+        // If they dont match
+        if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+            return 0;
+
+        // Filelength is upto extensions
+        file1Len = ext1Pos;
+        file2Len = ext2Pos;
+    }
+    // No extensions
+    else
+    {
+        // Filelength is actual filelength
+        file1Len = (int)strlen(strA);
+        file2Len = (int)strlen(strB);
+    }
+
+    // Find length without trailing spaces (before ext)
+    file1Len = FileString_TrimLength(strA, file1Len);
+    file2Len = FileString_TrimLength(strB, file2Len);
+
+    // Check the file lengths match
+    if (file1Len!=file2Len)
+        return 0;
+
+    // Compare main part of filenames
+    if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+        return 0;
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+    if (path)
+    {
+        while (*path)
+        {
+            // Last character?
+            if (!(*(path+1)))
+            {
+                if (*path == '\\' || *path == '/')
+                    return 1;
+            }
+
+            path++;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+    int len = 0;
+    while (*in && len <= 11)
+    {
+        char a = *in++;
+
+        if (a == ' ')
+            continue;
+        // Make lower case if uppercase
+        else if ((a>='A') && (a<='Z'))
+            a+= 32;
+
+        *out++ = a;
+        len++;
+    }
+
+    *out = '\0';
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+    int len = 0;
+
+    // Get files extension offset
+    int ext_pos = FileString_GetExtension(filename);
+
+    if (ext_pos > 0 && out && maxlen)
+    {
+        filename += ext_pos + 1;
+
+        while (*filename && len < (maxlen-1))
+        {
+            char a = *filename++;
+
+            // Make lowercase if uppercase
+            if ((a>='A') && (a<='Z'))
+                a+= 32;
+
+            *out++ = a;
+            len++;
+        }
+
+        *out = '\0';
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+    int len = 0;
+    char last = 0;
+    char seperator = '/';
+
+    if (path && filename && out && maxlen > 0)
+    {
+        while (*path && len < (maxlen-2))
+        {
+            last = *path++;
+            if (last == '\\')
+                seperator = '\\';
+            *out++ = last;
+            len++;
+        }
+
+        // Add a seperator if trailing one not found
+        if (last != '\\' && last != '/')
+            *out++ = seperator;
+
+        while (*filename && len < (maxlen-1))
+        {
+            *out++ = *filename++;
+            len++;
+        }
+
+        *out = '\0';
+
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+    char output[255];
+    char output2[255];
+
+    assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+    assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+    assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+    assert(fatfs_total_path_levels("C:\\") == -1);
+    assert(fatfs_total_path_levels("") == -1);
+    assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+    assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "folder") == 0);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "dev") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "etc") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "C:\\folder") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(output[0] == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "/dev/etc") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+    assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+    assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+    assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("C:\\file.zip   ", strlen("C:\\file.zip   ")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("   ", strlen("   ")) == 0);
+
+    assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file2.ext") == 0);
+
+    assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+    assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+    assert(fatfs_string_ends_with_slash("/path") == 0);
+    assert(fatfs_string_ends_with_slash("/path/a") == 0);
+    assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+    assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+    assert(strcmp(output, "ext") != 0);
+
+    assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+    assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+    assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c
new file mode 100644 (file)
index 0000000..8d05d3b
--- /dev/null
@@ -0,0 +1,478 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+    #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+//                            FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location)        ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location)        ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+    int i;
+
+    // FAT buffer chain head
+    fs->fat_buffer_head = NULL;
+
+    for (i=0;i<FAT_BUFFERS;i++)
+    {
+        // Initialise buffers to invalid
+        fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+        fs->fat_buffers[i].dirty = 0;
+        memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+        fs->fat_buffers[i].ptr = NULL;
+
+        // Add to head of queue
+        fs->fat_buffers[i].next = fs->fat_buffer_head;
+        fs->fat_buffer_head = &fs->fat_buffers[i];
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+    if (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+        {
+            if (fs->disk_io.write_media)
+            {
+                uint32 sectors = FAT_BUFFER_SECTORS;
+                uint32 offset = pcur->address - fs->fat_begin_lba;
+
+                // Limit to sectors used for the FAT
+                if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+                    sectors = FAT_BUFFER_SECTORS;
+                else
+                    sectors = fs->fat_sectors - offset;
+
+                if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+                    return 0;
+            }
+
+            pcur->dirty = 0;
+        }
+
+        return 1;
+    }
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+    struct fat_buffer *last = NULL;
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Sector within this buffer?
+        if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+            break;
+
+        // End of list?
+        if (pcur->next == NULL)
+        {
+            // Remove buffer from list
+            if (last)
+                last->next = NULL;
+            // We the first and last buffer in the chain?
+            else
+                fs->fat_buffer_head = NULL;
+        }
+
+        last = pcur;
+        pcur = pcur->next;
+    }
+
+    // We found the sector already in FAT buffer chain
+    if (pcur)
+    {
+        pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+        return pcur;
+    }
+
+    // Else, we removed the last item from the list
+    pcur = last;
+
+    // Add to start of sector buffer list (now newest sector)
+    pcur->next = fs->fat_buffer_head;
+    fs->fat_buffer_head = pcur;
+
+    // Writeback sector if changed
+    if (pcur->dirty)
+        if (!fatfs_fat_writeback(fs, pcur))
+            return 0;
+
+    // Address is now new sector
+    pcur->address = sector;
+
+    // Read next sector
+    if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+    {
+        // Read failed, invalidate buffer address
+        pcur->address = FAT32_INVALID_CLUSTER;
+        return NULL;
+    }
+
+    pcur->ptr = pcur->sector;
+    return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+            if (!fatfs_fat_writeback(fs, pcur))
+                return 0;
+
+        pcur = pcur->next;
+    }
+
+    return 1;
+}
+
+//-----------------------------------------------------------------------------
+//                        General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    struct fat_buffer *pbuf;
+
+    // Why is '..' labelled with cluster 0 when it should be 2 ??
+    if (current_cluster == 0)
+        current_cluster = 2;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = current_cluster / 256;
+    else
+        fat_sector_offset = current_cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return (FAT32_LAST_CLUSTER);
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+        // If end of chain found
+        if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+        // Mask out MS 4 bits (its 28bit addressing)
+        nextcluster = nextcluster & 0x0FFFFFFF;
+
+        // If end of chain found
+        if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+
+    // Else return next cluster
+    return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        ;
+    else
+    {
+        // Load sector to change it
+        struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+        if (!pbuf)
+            return ;
+
+        // Change
+        FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+        fs->next_free_cluster = newValue;
+
+        // Write back FSINFO sector to disk
+        if (fs->disk_io.write_media)
+            fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+        // Invalidate cache entry
+        pbuf->address = FAT32_INVALID_CLUSTER;
+        pbuf->dirty = 0;
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    uint32 current_cluster = start_cluster;
+    struct fat_buffer *pbuf;
+
+    do
+    {
+        // Find which sector of FAT table to read
+        if (fs->fat_type == FAT_TYPE_16)
+            fat_sector_offset = current_cluster / 256;
+        else
+            fat_sector_offset = current_cluster / 128;
+
+        if ( fat_sector_offset < fs->fat_sectors)
+        {
+            // Read FAT sector into buffer
+            pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+            if (!pbuf)
+                return 0;
+
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+            }
+            else
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+                // Mask out MS 4 bits (its 28bit addressing)
+                nextcluster = nextcluster & 0x0FFFFFFF;
+            }
+
+            if (nextcluster !=0 )
+                current_cluster++;
+        }
+        else
+            // Otherwise, run out of FAT sectors to check...
+            return 0;
+    }
+    while (nextcluster != 0x0);
+
+    // Found blank entry
+    *free_cluster = current_cluster;
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+    struct fat_buffer *pbuf;
+    uint32 fat_sector_offset, position;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = cluster / 256;
+    else
+        fat_sector_offset = cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return 0;
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 16 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 256)) * 2;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 128)) * 4;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+    uint32 last_cluster;
+    uint32 next_cluster = start_cluster;
+
+    // Loop until end of chain
+    while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+        // Clear last link
+        fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+    uint32 last_cluster = FAT32_LAST_CLUSTER;
+    uint32 next_cluster = start_cluster;
+
+    if (start_cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Loop until end of chain
+    while ( next_cluster != FAT32_LAST_CLUSTER )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+        if (!next_cluster)
+            return 0;
+    }
+
+    // Add link in for new cluster
+    fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+    // Mark new cluster as end of chain
+    fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+    uint32 i,j;
+    uint32 count = 0;
+    struct fat_buffer *pbuf;
+
+    for (i = 0; i < fs->fat_sectors; i++)
+    {
+        // Read FAT sector into buffer
+        pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+        if (!pbuf)
+            break;
+
+        for (j = 0; j < FAT_SECTOR_SIZE; )
+        {
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 2;
+            }
+            else
+            {
+                if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 4;
+            }
+        }
+    }
+
+    return count;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c
new file mode 100644 (file)
index 0000000..0718cb1
--- /dev/null
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+    uint32 i;
+    uint32 nextcluster;
+    uint32 start = *startCluster;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    for (i=0;i<clusters;i++)
+    {
+        // Start looking for free clusters from the beginning
+        if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+        {
+            // Point last to this
+            fatfs_fat_set_cluster(fs, start, nextcluster);
+
+            // Point this to end of file
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+            // Adjust argument reference
+            start = nextcluster;
+            if (i == 0)
+                *startCluster = nextcluster;
+        }
+        else
+            return 0;
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+    uint32 clusterSize;
+    uint32 clusterCount;
+    uint32 nextcluster;
+
+    if (size==0)
+        return 0;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    // Work out size and clusters
+    clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+    clusterCount = (size / clusterSize);
+
+    // If any left over
+    if (size-(clusterSize*clusterCount))
+        clusterCount++;
+
+    // Allocated first link in the chain if a new file
+    if (newFile)
+    {
+        if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+            return 0;
+
+        // If this is all that is needed then all done
+        if (clusterCount==1)
+        {
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+            *startCluster = nextcluster;
+            return 1;
+        }
+    }
+    // Allocate from end of current chain (startCluster is end of chain)
+    else
+        nextcluster = *startCluster;
+
+    if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+    struct fat_dir_entry *directoryEntry;
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    int possible_spaces = 0;
+    int start_recorded = 0;
+
+    // No entries required?
+    if (entryCount == 0)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+                // LFN Entry
+                if (fatfs_entry_lfn_text(directoryEntry))
+                {
+                    // First entry?
+                    if (possible_spaces == 0)
+                    {
+                        // Store start
+                        *pSector = x-1;
+                        *pOffset = item;
+                        start_recorded = 1;
+                    }
+
+                    // Increment the count in-case the file turns
+                    // out to be deleted...
+                    possible_spaces++;
+                }
+                // SFN Entry
+                else
+                {
+                    // Has file been deleted?
+                    if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+
+                        // Else continue counting until we find a valid entry!
+                    }
+                    // Is the file entry empty?
+                    else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        // Increment the blank entries count
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+                    }
+                    // File entry is valid
+                    else
+                    {
+                        // Reset all flags
+                        possible_spaces = 0;
+                        start_recorded = 0;
+                    }
+                }
+            } // End of for
+        } // End of if
+        // Run out of free space in the directory, allocate some more
+        else
+        {
+            uint32 newCluster;
+
+            // Get a new cluster for directory
+            if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+                return 0;
+
+            // Add cluster to end of directory tree
+            if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+                return 0;
+
+            // Erase new directory cluster
+            memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+            for (i=0;i<fs->sectors_per_cluster;i++)
+            {
+                if (!fatfs_write_sector(fs, newCluster, i, 0))
+                    return 0;
+            }
+
+            // If non of the name fitted on previous sectors
+            if (!start_recorded)
+            {
+                // Store start
+                *pSector = (x-1);
+                *pOffset = 0;
+                start_recorded = 1;
+            }
+
+            return 1;
+        }
+    } // End of while loop
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    uint32 x=0;
+    int entryCount;
+    struct fat_dir_entry shortEntry;
+    int dirtySector = 0;
+
+    uint32 dirSector = 0;
+    uint8 dirOffset = 0;
+    int foundEnd = 0;
+
+    uint8 checksum;
+    uint8 *pSname;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+    // How many LFN entries are required?
+    // NOTE: We always request one LFN even if it would fit in a SFN!
+    entryCount = fatfs_lfn_entries_required(filename);
+    if (!entryCount)
+        return 0;
+#else
+    entryCount = 0;
+#endif
+
+    // Find space in the directory for this filename (or allocate some more)
+    // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+    if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+        return 0;
+
+    // Generate checksum of short filename
+    pSname = (uint8*)shortfilename;
+    checksum = 0;
+    for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+    // Start from current sector where space was found!
+    x = dirSector;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // If the start position for the entry has been found
+                if (foundEnd==0)
+                    if ( (dirSector==(x-1)) && (dirOffset==item) )
+                        foundEnd = 1;
+
+                // Start adding filename
+                if (foundEnd)
+                {
+                    if (entryCount==0)
+                    {
+                        // Short filename
+                        fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update create, access & modify time & date
+                        fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+                        memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+                        // Writeback
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+#if FATFS_INC_LFN_SUPPORT
+                    else
+                    {
+                        entryCount--;
+
+                        // Copy entry to directory buffer
+                        fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+                        dirtySector = 1;
+                    }
+#endif
+                }
+            } // End of if
+
+            // Write back to disk before loading another sector
+            if (dirtySector)
+            {
+                if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+                    return 0;
+
+                dirtySector = 0;
+            }
+        }
+        else
+            return 0;
+    } // End of while loop
+
+    return 0;
+}
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt
new file mode 100644 (file)
index 0000000..5a00a98
--- /dev/null
@@ -0,0 +1 @@
+2.6.11\r
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh b/LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh
new file mode 100644 (file)
index 0000000..07c542f
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# https://github.com/lammertb/libhttp/archive/v1.8.tar.gz
+
+
+# rm -rf include
+# rm -rf lib
+# mkdir include
+# mkdir lib
+
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# cp -a include/civetweb.h  ../include/
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_64.a
+
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib COPT="-m32 -DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_32.a
+
+
+
+# cd ..
+# rm -rf libhttp-1.8
+# tar xf libhttp-1.8.tar.gz
+# cd libhttp-1.8
+# make lib CC=aarch64-linux-gnu-gcc  COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
+# cp -a libcivetweb.a ../lib/libcivetweb_aa64.a
+
+
+# cd ..
+# rm -rf libhttp-1.8
+
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c
new file mode 100644 (file)
index 0000000..98130f5
--- /dev/null
@@ -0,0 +1,13145 @@
+/* Copyright (c) 2013-2016 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * 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.
+ */
+
+
+#if defined(_WIN32)
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
+#endif
+#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */
+#define _WIN32_WINNT 0x0501
+#endif
+#else
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE /* for setgroups() */
+#endif
+#if defined(__linux__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
+#endif
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
+#endif
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
+#endif
+#ifdef __sun
+#define __EXTENSIONS__  /* to expose flockfile and friends in stdio.h */
+#define __inline inline /* not recognized on older compiler versions */
+#endif
+#endif
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+#define USE_TIMERS
+#endif
+
+#if defined(_MSC_VER)
+/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
+#pragma warning(disable : 4306)
+/* conditional expression is constant: introduced by FD_SET(..) */
+#pragma warning(disable : 4127)
+/* non-constant aggregate initializer: issued due to missing C99 support */
+#pragma warning(disable : 4204)
+/* padding added after data member */
+#pragma warning(disable : 4820)
+/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
+#pragma warning(disable : 4668)
+/* no function prototype given: converting '()' to '(void)' */
+#pragma warning(disable : 4255)
+/* function has been selected for automatic inline expansion */
+#pragma warning(disable : 4711)
+#endif
+
+
+/* This code uses static_assert to check some conditions.
+ * Unfortunately some compilers still do not support it, so we have a
+ * replacement function here. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define mg_static_assert static_assert
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define mg_static_assert static_assert
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define mg_static_assert _Static_assert
+#else
+char static_assert_replacement[1];
+#define mg_static_assert(cond, txt)                                            \
+       extern char static_assert_replacement[(cond) ? 1 : -1]
+#endif
+
+mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
+                 "int data type size check");
+mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
+                 "pointer data type size check");
+mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
+/* mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t data
+ * type size check"); */
+
+/* DTL -- including winsock2.h works better if lean and mean */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(__SYMBIAN32__)
+#define NO_SSL /* SSL is not supported */
+#define NO_CGI /* CGI is not supported */
+#define PATH_MAX FILENAME_MAX
+#endif /* __SYMBIAN32__ */
+
+
+/* Include the header file here, so the CivetWeb interface is defined for the
+ * entire implementation, including the following forward definitions. */
+#include "civetweb.h"
+
+
+#ifndef IGNORE_UNUSED_RESULT
+#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
+#endif
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+#ifdef __MACH__
+
+#define CLOCK_MONOTONIC (1)
+#define CLOCK_REALTIME (2)
+
+#include <sys/time.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <assert.h>
+
+
+/* clock_gettime is not implemented on OSX */
+int clock_gettime(int clk_id, struct timespec *t);
+
+int
+clock_gettime(int clk_id, struct timespec *t)
+{
+       memset(t, 0, sizeof(*t));
+       if (clk_id == CLOCK_REALTIME) {
+               struct timeval now;
+               int rv = gettimeofday(&now, NULL);
+               if (rv) {
+                       return rv;
+               }
+               t->tv_sec = now.tv_sec;
+               t->tv_nsec = now.tv_usec * 1000;
+               return 0;
+
+       } else if (clk_id == CLOCK_MONOTONIC) {
+               static uint64_t clock_start_time = 0;
+               static mach_timebase_info_data_t timebase_ifo = {0, 0};
+
+               uint64_t now = mach_absolute_time();
+
+               if (clock_start_time == 0) {
+                       kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
+#if defined(DEBUG)
+                       assert(mach_status == KERN_SUCCESS);
+#else
+                       /* appease "unused variable" warning for release builds */
+                       (void)mach_status;
+#endif
+                       clock_start_time = now;
+               }
+
+               now = (uint64_t)((double)(now - clock_start_time)
+                                * (double)timebase_ifo.numer
+                                / (double)timebase_ifo.denom);
+
+               t->tv_sec = now / 1000000000;
+               t->tv_nsec = now % 1000000000;
+               return 0;
+       }
+       return -1; /* EINVAL - Clock ID is unknown */
+}
+#endif
+
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+
+
+#ifndef MAX_WORKER_THREADS
+#define MAX_WORKER_THREADS (1024 * 64)
+#endif
+#ifndef SOCKET_TIMEOUT_QUANTUM
+#define SOCKET_TIMEOUT_QUANTUM (10000)
+#endif
+
+mg_static_assert(MAX_WORKER_THREADS >= 1,
+                 "worker threads must be a positive number");
+
+#if defined(_WIN32)                                                            \
+    && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */
+#include <windows.h>
+#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
+#include <ws2tcpip.h>
+
+typedef const char *SOCK_OPT_TYPE;
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (MAX_PATH)
+#endif
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (4096)
+#endif
+
+mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
+
+#ifndef _IN_PORT_T
+#ifndef in_port_t
+#define in_port_t u_short
+#endif
+#endif
+
+#ifndef _WIN32_WCE
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else          /* _WIN32_WCE */
+#define NO_CGI /* WinCE has no pipes */
+
+typedef long off_t;
+
+#define errno ((int)(GetLastError()))
+#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
+#endif /* _WIN32_WCE */
+
+#define MAKEUQUAD(lo, hi)                                                      \
+       ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
+#define RATE_DIFF (10000000) /* 100 nsecs */
+#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
+#define SYS2UNIX_TIME(lo, hi)                                                  \
+       ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
+
+/* Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers */
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
+#define strtoll(x, y, z) (_atoi64(x))
+#else
+#define __func__ __FUNCTION__
+#define strtoull(x, y, z) (_strtoui64(x, y, z))
+#define strtoll(x, y, z) (_strtoi64(x, y, z))
+#endif
+#endif /* _MSC_VER */
+
+#define ERRNO ((int)(GetLastError()))
+#define NO_SOCKLEN_T
+
+#if defined(_WIN64) || defined(__MINGW64__)
+#define SSL_LIB "ssleay64.dll"
+#define CRYPTO_LIB "libeay64.dll"
+#else
+#define SSL_LIB "ssleay32.dll"
+#define CRYPTO_LIB "libeay32.dll"
+#endif
+
+#define O_NONBLOCK (0)
+#ifndef W_OK
+#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
+#endif
+#if !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif /* !EWOULDBLOCK */
+#define _POSIX_
+#define INT64_FMT "I64d"
+#define UINT64_FMT "I64u"
+
+#define WINCDECL __cdecl
+#define SHUT_RD (0)
+#define SHUT_WR (1)
+#define SHUT_BOTH (2)
+#define vsnprintf_impl _vsnprintf
+#define access _access
+#define mg_sleep(x) (Sleep(x))
+
+#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
+#ifndef popen
+#define popen(x, y) (_popen(x, y))
+#endif
+#ifndef pclose
+#define pclose(x) (_pclose(x))
+#endif
+#define close(x) (_close(x))
+#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
+#define RTLD_LAZY (0)
+#define fseeko(x, y, z) (_lseeki64(_fileno(x), (y), (z)) == -1 ? -1 : 0)
+#define fdopen(x, y) (_fdopen((x), (y)))
+#define write(x, y, z) (_write((x), (y), (unsigned)z))
+#define read(x, y, z) (_read((x), (y), (unsigned)z))
+#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
+#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
+#define sleep(x) (Sleep((x)*1000))
+#define rmdir(x) (_rmdir(x))
+#define timegm(x) (_mkgmtime(x))
+
+#if !defined(fileno)
+#define fileno(x) (_fileno(x))
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef DWORD pthread_key_t;
+typedef HANDLE pthread_t;
+typedef struct {
+       CRITICAL_SECTION threadIdSec;
+       int waitingthreadcount;       /* The number of threads queued. */
+       pthread_t *waitingthreadhdls; /* The thread handles. */
+} pthread_cond_t;
+
+#ifndef __clockid_t_defined
+typedef DWORD clockid_t;
+#endif
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC (1)
+#endif
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME (2)
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define _TIMESPEC_DEFINED
+#endif
+#ifndef _TIMESPEC_DEFINED
+struct timespec {
+       time_t tv_sec; /* seconds */
+       long tv_nsec;  /* nanoseconds */
+};
+#endif
+
+#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+static void path_to_unicode(const struct mg_connection *conn,
+                            const char *path,
+                            wchar_t *wbuf,
+                            size_t wbuf_len);
+struct file;
+static const char *
+mg_fgets(char *buf, size_t size, struct file *filep, char **p);
+
+
+#if defined(HAVE_STDINT)
+#include <stdint.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 int64_t;
+#define INT64_MAX (9223372036854775807)
+#endif /* HAVE_STDINT */
+
+/* POSIX dirent interface */
+struct dirent {
+       char d_name[PATH_MAX];
+};
+
+typedef struct DIR {
+       HANDLE handle;
+       WIN32_FIND_DATAW info;
+       struct dirent result;
+} DIR;
+
+#if defined(_WIN32) && !defined(POLLIN)
+#ifndef HAVE_POLL
+struct pollfd {
+       SOCKET fd;
+       short events;
+       short revents;
+};
+#define POLLIN (0x0300)
+#endif
+#endif
+
+/* Mark required libraries */
+#if defined(_MSC_VER)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#else /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include   \
+         block */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+typedef const void *SOCK_OPT_TYPE;
+
+#if defined(ANDROID)
+typedef unsigned short int in_port_t;
+#endif
+
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#define vsnprintf_impl vsnprintf
+
+#if !defined(NO_SSL_DL) && !defined(NO_SSL)
+#include <dlfcn.h>
+#endif
+#include <pthread.h>
+#if defined(__MACH__)
+#define SSL_LIB "libssl.dylib"
+#define CRYPTO_LIB "libcrypto.dylib"
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "libssl.so"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libcrypto.so"
+#endif
+#endif
+#ifndef O_BINARY
+#define O_BINARY (0)
+#endif /* O_BINARY */
+#define closesocket(a) (close(a))
+#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
+#define mg_remove(conn, x) (remove(x))
+#define mg_sleep(x) (usleep((x)*1000))
+#define mg_opendir(conn, x) (opendir(x))
+#define mg_closedir(x) (closedir(x))
+#define mg_readdir(x) (readdir(x))
+#define ERRNO (errno)
+#define INVALID_SOCKET (-1)
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+typedef int SOCKET;
+#define WINCDECL
+
+#if defined(__hpux)
+/* HPUX 11 does not have monotonic, fall back to realtime */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
+ * Itanium.  Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
+ * the prototypes use int* rather than socklen_t* which matches the
+ * actual library expectation.  When called with the wrong size arg
+ * accept() returns a zero client inet addr and check_acl() always
+ * fails.  Since socklen_t is widely used below, just force replace
+ * their typedef with int. - DTL
+ */
+#define socklen_t int
+#endif /* hpux */
+
+#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include  \
+          block */
+
+/* va_copy should always be a macro, C99 and C++11 - DTL */
+#ifndef va_copy
+#define va_copy(x, y) ((x) = (y))
+#endif
+
+#ifdef _WIN32
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static CRITICAL_SECTION global_log_file_lock;
+static DWORD
+pthread_self(void)
+{
+       return GetCurrentThreadId();
+}
+
+
+static int
+pthread_key_create(
+    pthread_key_t *key,
+    void (*_ignored)(void *) /* destructor not supported for Windows */
+    )
+{
+       (void)_ignored;
+
+       if ((key != 0)) {
+               *key = TlsAlloc();
+               return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
+       }
+       return -2;
+}
+
+
+static int
+pthread_key_delete(pthread_key_t key)
+{
+       return TlsFree(key) ? 0 : 1;
+}
+
+
+static int
+pthread_setspecific(pthread_key_t key, void *value)
+{
+       return TlsSetValue(key, value) ? 0 : 1;
+}
+
+
+static void *
+pthread_getspecific(pthread_key_t key)
+{
+       return TlsGetValue(key);
+}
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
+#else
+static pthread_mutexattr_t pthread_mutex_attr;
+#endif /* _WIN32 */
+
+
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#define CGI_ENVIRONMENT_SIZE (4096)
+#define MAX_CGI_ENVIR_VARS (256)
+#define MG_BUF_LEN (8192)
+
+#ifndef MAX_REQUEST_SIZE
+#define MAX_REQUEST_SIZE (16384)
+#endif
+
+mg_static_assert(MAX_REQUEST_SIZE >= 256,
+                 "request size length must be a positive number");
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+#if !defined(DEBUG_TRACE)
+#if defined(DEBUG)
+
+
+static void DEBUG_TRACE_FUNC(const char *func,
+                             unsigned line,
+                             PRINTF_FORMAT_STRING(const char *fmt),
+                             ...) PRINTF_ARGS(3, 4);
+
+static void
+DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
+{
+       va_list args;
+       flockfile(stdout);
+       printf("*** %lu.%p.%s.%u: ",
+              (unsigned long)time(NULL),
+              (void *)pthread_self(),
+              func,
+              line);
+       va_start(args, fmt);
+       vprintf(fmt, args);
+       va_end(args);
+       putchar('\n');
+       fflush(stdout);
+       funlockfile(stdout);
+}
+
+#define DEBUG_TRACE(fmt, ...)                                                  \
+       DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
+
+#else
+#define DEBUG_TRACE(fmt, ...)                                                  \
+       do {                                                                       \
+       } while (0)
+#endif /* DEBUG */
+#endif /* DEBUG_TRACE */
+
+#if defined(MEMORY_DEBUGGING)
+unsigned long mg_memory_debug_blockCount = 0;
+unsigned long mg_memory_debug_totalMemUsed = 0;
+
+
+static void *
+mg_malloc_ex(size_t size, const char *file, unsigned line)
+{
+       void *data = malloc(size + sizeof(size_t));
+       void *memory = 0;
+       char mallocStr[256];
+
+       if (data) {
+               *(size_t *)data = size;
+               mg_memory_debug_totalMemUsed += size;
+               mg_memory_debug_blockCount++;
+               memory = (void *)(((char *)data) + sizeof(size_t));
+       }
+
+       sprintf(mallocStr,
+               "MEM: %p %5lu alloc   %7lu %4lu --- %s:%u\n",
+               memory,
+               (unsigned long)size,
+               mg_memory_debug_totalMemUsed,
+               mg_memory_debug_blockCount,
+               file,
+               line);
+#if defined(_WIN32)
+       OutputDebugStringA(mallocStr);
+#else
+       DEBUG_TRACE("%s", mallocStr);
+#endif
+
+       return memory;
+}
+
+
+static void *
+mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
+{
+       void *data = mg_malloc_ex(size * count, file, line);
+       if (data) {
+               memset(data, 0, size);
+       }
+       return data;
+}
+
+
+static void
+mg_free_ex(void *memory, const char *file, unsigned line)
+{
+       char mallocStr[256];
+       void *data = (void *)(((char *)memory) - sizeof(size_t));
+       size_t size;
+
+       if (memory) {
+               size = *(size_t *)data;
+               mg_memory_debug_totalMemUsed -= size;
+               mg_memory_debug_blockCount--;
+               sprintf(mallocStr,
+                       "MEM: %p %5lu free    %7lu %4lu --- %s:%u\n",
+                       memory,
+                       (unsigned long)size,
+                       mg_memory_debug_totalMemUsed,
+                       mg_memory_debug_blockCount,
+                       file,
+                       line);
+#if defined(_WIN32)
+               OutputDebugStringA(mallocStr);
+#else
+               DEBUG_TRACE("%s", mallocStr);
+#endif
+
+               free(data);
+       }
+}
+
+
+static void *
+mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
+{
+       char mallocStr[256];
+       void *data;
+       void *_realloc;
+       size_t oldsize;
+
+       if (newsize) {
+               if (memory) {
+                       data = (void *)(((char *)memory) - sizeof(size_t));
+                       oldsize = *(size_t *)data;
+                       _realloc = realloc(data, newsize + sizeof(size_t));
+                       if (_realloc) {
+                               data = _realloc;
+                               mg_memory_debug_totalMemUsed -= oldsize;
+                               sprintf(mallocStr,
+                                       "MEM: %p %5lu r-free  %7lu %4lu --- %s:%u\n",
+                                       memory,
+                                       (unsigned long)oldsize,
+                                       mg_memory_debug_totalMemUsed,
+                                       mg_memory_debug_blockCount,
+                                       file,
+                                       line);
+#if defined(_WIN32)
+                               OutputDebugStringA(mallocStr);
+#else
+                               DEBUG_TRACE("%s", mallocStr);
+#endif
+                               mg_memory_debug_totalMemUsed += newsize;
+                               sprintf(mallocStr,
+                                       "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
+                                       memory,
+                                       (unsigned long)newsize,
+                                       mg_memory_debug_totalMemUsed,
+                                       mg_memory_debug_blockCount,
+                                       file,
+                                       line);
+#if defined(_WIN32)
+                               OutputDebugStringA(mallocStr);
+#else
+                               DEBUG_TRACE("%s", mallocStr);
+#endif
+                               *(size_t *)data = newsize;
+                               data = (void *)(((char *)data) + sizeof(size_t));
+                       } else {
+#if defined(_WIN32)
+                               OutputDebugStringA("MEM: realloc failed\n");
+#else
+                               DEBUG_TRACE("%s", "MEM: realloc failed\n");
+#endif
+                               return _realloc;
+                       }
+               } else {
+                       data = mg_malloc_ex(newsize, file, line);
+               }
+       } else {
+               data = 0;
+               mg_free_ex(memory, file, line);
+       }
+
+       return data;
+}
+
+#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__)
+#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__)
+#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__)
+#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
+
+#else
+
+static __inline void *
+mg_malloc(size_t a)
+{
+       return malloc(a);
+}
+
+static __inline void *
+mg_calloc(size_t a, size_t b)
+{
+       return calloc(a, b);
+}
+
+static __inline void *
+mg_realloc(void *a, size_t b)
+{
+       return realloc(a, b);
+}
+
+static __inline void
+mg_free(void *a)
+{
+       free(a);
+}
+
+#endif
+
+
+static void mg_vsnprintf(const struct mg_connection *conn,
+                         int *truncated,
+                         char *buf,
+                         size_t buflen,
+                         const char *fmt,
+                         va_list ap);
+
+static void mg_snprintf(const struct mg_connection *conn,
+                        int *truncated,
+                        char *buf,
+                        size_t buflen,
+                        PRINTF_FORMAT_STRING(const char *fmt),
+                        ...) PRINTF_ARGS(5, 6);
+
+/* This following lines are just meant as a reminder to use the mg-functions
+ * for memory management */
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef calloc
+#undef calloc
+#endif
+#ifdef realloc
+#undef realloc
+#endif
+#ifdef free
+#undef free
+#endif
+#ifdef snprintf
+#undef snprintf
+#endif
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
+#ifdef _WIN32 /* vsnprintf must not be used in any system, *                   \
+               * but this define only works well for Windows. */
+#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
+#endif
+
+#define MD5_STATIC static
+#include "md5.inl"
+
+/* Darwin prior to 7.0 and Win32 do not have socklen_t */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+#define _DARWIN_UNLIMITED_SELECT
+
+#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
+
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL (0)
+#endif
+
+#if !defined(SOMAXCONN)
+#define SOMAXCONN (100)
+#endif
+
+/* Size of the accepted socket queue */
+#if !defined(MGSQLEN)
+#define MGSQLEN (20)
+#endif
+
+#if defined(NO_SSL_DL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#else
+/* SSL loaded dynamically from DLL.
+ * I put the prototypes here to be independent from OpenSSL source
+ * installation. */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+#define SSL_CTRL_OPTIONS (32)
+#define SSL_CTRL_CLEAR_OPTIONS (77)
+#define SSL_CTRL_SET_ECDH_AUTO (94)
+
+#define SSL_VERIFY_NONE (0)
+#define SSL_VERIFY_PEER (1)
+#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
+#define SSL_VERIFY_CLIENT_ONCE (4)
+#define SSL_OP_ALL ((long)(0x80000BFFUL))
+#define SSL_OP_NO_SSLv2 (0x01000000L)
+#define SSL_OP_NO_SSLv3 (0x02000000L)
+#define SSL_OP_NO_TLSv1 (0x04000000L)
+#define SSL_OP_NO_TLSv1_2 (0x08000000L)
+#define SSL_OP_NO_TLSv1_1 (0x10000000L)
+#define SSL_OP_SINGLE_DH_USE (0x00100000L)
+
+struct ssl_func {
+       const char *name;  /* SSL function name */
+       void (*ptr)(void); /* Function pointer */
+};
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file                                            \
+       (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file                                           \
+       (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb                                          \
+       (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
+#define SSL_CTX_use_certificate_chain_file                                     \
+       (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
+#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
+#define SSL_CTX_set_verify                                                     \
+       (*(void (*)(SSL_CTX *,                                                     \
+                   int,                                                           \
+                   int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations                                          \
+       (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
+#define SSL_CIPHER_get_name                                                    \
+       (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
+#define SSL_CTX_set_session_id_context                                         \
+       (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
+#define SSL_CTX_set_cipher_list                                                \
+       (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
+#define SSL_CTX_set_options(ctx, op)                                           \
+       SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
+#define SSL_CTX_clear_options(ctx, op)                                         \
+       SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff)                                      \
+       SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
+#define CRYPTO_set_locking_callback                                            \
+       (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
+#define CRYPTO_set_id_callback                                                 \
+       (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
+#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
+#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
+#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
+#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
+
+
+/* set_ssl_option() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
+                                   {"SSL_accept", NULL},
+                                   {"SSL_connect", NULL},
+                                   {"SSL_read", NULL},
+                                   {"SSL_write", NULL},
+                                   {"SSL_get_error", NULL},
+                                   {"SSL_set_fd", NULL},
+                                   {"SSL_new", NULL},
+                                   {"SSL_CTX_new", NULL},
+                                   {"SSLv23_server_method", NULL},
+                                   {"SSL_library_init", NULL},
+                                   {"SSL_CTX_use_PrivateKey_file", NULL},
+                                   {"SSL_CTX_use_certificate_file", NULL},
+                                   {"SSL_CTX_set_default_passwd_cb", NULL},
+                                   {"SSL_CTX_free", NULL},
+                                   {"SSL_load_error_strings", NULL},
+                                   {"SSL_CTX_use_certificate_chain_file", NULL},
+                                   {"SSLv23_client_method", NULL},
+                                   {"SSL_pending", NULL},
+                                   {"SSL_CTX_set_verify", NULL},
+                                   {"SSL_shutdown", NULL},
+                                   {"SSL_CTX_load_verify_locations", NULL},
+                                   {"SSL_CTX_set_default_verify_paths", NULL},
+                                   {"SSL_CTX_set_verify_depth", NULL},
+                                   {"SSL_get_peer_certificate", NULL},
+                                   {"SSL_get_version", NULL},
+                                   {"SSL_get_current_cipher", NULL},
+                                   {"SSL_CIPHER_get_name", NULL},
+                                   {"SSL_CTX_check_private_key", NULL},
+                                   {"SSL_CTX_set_session_id_context", NULL},
+                                   {"SSL_CTX_ctrl", NULL},
+                                   {"SSL_CTX_set_cipher_list", NULL},
+                                   {NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+#if !defined(NO_SSL)
+static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
+                                      {"CRYPTO_set_locking_callback", NULL},
+                                      {"CRYPTO_set_id_callback", NULL},
+                                      {"ERR_get_error", NULL},
+                                      {"ERR_error_string", NULL},
+                                      {"ERR_remove_state", NULL},
+                                      {"ERR_free_strings", NULL},
+                                      {"ENGINE_cleanup", NULL},
+                                      {"CONF_modules_unload", NULL},
+                                      {"CRYPTO_cleanup_all_ex_data", NULL},
+                                      {"EVP_cleanup", NULL},
+                                      {NULL, NULL}};
+#endif /* NO_SSL */
+#endif /* NO_SSL_DL */
+
+
+#if !defined(NO_CACHING)
+static const char *month_names[] = {"Jan",
+                                    "Feb",
+                                    "Mar",
+                                    "Apr",
+                                    "May",
+                                    "Jun",
+                                    "Jul",
+                                    "Aug",
+                                    "Sep",
+                                    "Oct",
+                                    "Nov",
+                                    "Dec"};
+#endif /* !NO_CACHING */
+
+/* Unified socket address. For IPv6 support, add IPv6 address structure in the
+ * union u. */
+union usa {
+       struct sockaddr sa;
+       struct sockaddr_in sin;
+#if defined(USE_IPV6)
+       struct sockaddr_in6 sin6;
+#endif
+};
+
+/* Describes a string (chunk of memory). */
+struct vec {
+       const char *ptr;
+       size_t len;
+};
+
+struct file {
+       uint64_t size;
+       time_t last_modified;
+       FILE *fp;
+       const char *membuf; /* Non-NULL if file data is in memory */
+       int is_directory;
+       int gzipped; /* set to 1 if the content is gzipped
+                     * in which case we need a content-encoding: gzip header */
+};
+
+#define STRUCT_FILE_INITIALIZER                                                \
+       {                                                                          \
+               (uint64_t)0, (time_t)0, (FILE *)NULL, (const char *)NULL, 0, 0         \
+       }
+
+/* Describes listening socket, or socket which was accept()-ed by the master
+ * thread and queued for future handling by the worker thread. */
+struct socket {
+       SOCKET sock;             /* Listening socket */
+       union usa lsa;           /* Local socket address */
+       union usa rsa;           /* Remote socket address */
+       unsigned char is_ssl;    /* Is port SSL-ed */
+       unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
+                                 * port */
+};
+
+/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */
+enum {
+       CGI_EXTENSIONS,
+       CGI_ENVIRONMENT,
+       PUT_DELETE_PASSWORDS_FILE,
+       CGI_INTERPRETER,
+       PROTECT_URI,
+       AUTHENTICATION_DOMAIN,
+       SSI_EXTENSIONS,
+       THROTTLE,
+       ACCESS_LOG_FILE,
+       ENABLE_DIRECTORY_LISTING,
+       ERROR_LOG_FILE,
+       GLOBAL_PASSWORDS_FILE,
+       INDEX_FILES,
+       ENABLE_KEEP_ALIVE,
+       ACCESS_CONTROL_LIST,
+       EXTRA_MIME_TYPES,
+       LISTENING_PORTS,
+       DOCUMENT_ROOT,
+       SSL_CERTIFICATE,
+       NUM_THREADS,
+       RUN_AS_USER,
+       REWRITE,
+       HIDE_FILES,
+       REQUEST_TIMEOUT,
+       SSL_DO_VERIFY_PEER,
+       SSL_CA_PATH,
+       SSL_CA_FILE,
+       SSL_VERIFY_DEPTH,
+       SSL_DEFAULT_VERIFY_PATHS,
+       SSL_CIPHER_LIST,
+       SSL_PROTOCOL_VERSION,
+       SSL_SHORT_TRUST,
+#if defined(USE_WEBSOCKET)
+       WEBSOCKET_TIMEOUT,
+#endif
+       DECODE_URL,
+
+#if defined(USE_LUA)
+       LUA_PRELOAD_FILE,
+       LUA_SCRIPT_EXTENSIONS,
+       LUA_SERVER_PAGE_EXTENSIONS,
+#endif
+#if defined(USE_DUKTAPE)
+       DUKTAPE_SCRIPT_EXTENSIONS,
+#endif
+
+#if defined(USE_WEBSOCKET)
+       WEBSOCKET_ROOT,
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       LUA_WEBSOCKET_EXTENSIONS,
+#endif
+       ACCESS_CONTROL_ALLOW_ORIGIN,
+       ERROR_PAGES,
+       CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
+                         * socket option typedef TCP_NODELAY. */
+#if !defined(NO_CACHING)
+       STATIC_FILE_MAX_AGE,
+#endif
+
+       NUM_OPTIONS
+};
+
+
+/* Config option name, config types, default value */
+static struct mg_option config_options[] = {
+    {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+    {"cgi_environment", CONFIG_TYPE_STRING, NULL},
+    {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"cgi_interpreter", CONFIG_TYPE_FILE, NULL},
+    {"protect_uri", CONFIG_TYPE_STRING, NULL},
+    {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"},
+    {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
+    {"throttle", CONFIG_TYPE_STRING, NULL},
+    {"access_log_file", CONFIG_TYPE_FILE, NULL},
+    {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"error_log_file", CONFIG_TYPE_FILE, NULL},
+    {"global_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"index_files",
+     CONFIG_TYPE_STRING,
+#ifdef USE_LUA
+     "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,"
+     "index.shtml,index.php"},
+#else
+     "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
+#endif
+    {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"},
+    {"access_control_list", CONFIG_TYPE_STRING, NULL},
+    {"extra_mime_types", CONFIG_TYPE_STRING, NULL},
+    {"listening_ports", CONFIG_TYPE_STRING, "8080"},
+    {"document_root", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_certificate", CONFIG_TYPE_FILE, NULL},
+    {"num_threads", CONFIG_TYPE_NUMBER, "50"},
+    {"run_as_user", CONFIG_TYPE_STRING, NULL},
+    {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL},
+    {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+    {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
+    {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
+    {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
+    {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL},
+    {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"},
+    {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"},
+#if defined(USE_WEBSOCKET)
+    {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+#endif
+    {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"},
+
+#if defined(USE_LUA)
+    {"lua_preload_file", CONFIG_TYPE_FILE, NULL},
+    {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+    {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
+#endif
+#if defined(USE_DUKTAPE)
+    /* The support for duktape is still in alpha version state.
+     * The name of this config option might change. */
+    {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
+#endif
+
+#if defined(USE_WEBSOCKET)
+    {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+    {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+#endif
+    {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"},
+    {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
+    {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
+#if !defined(NO_CACHING)
+    {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"},
+#endif
+
+    {NULL, CONFIG_TYPE_UNKNOWN, NULL}};
+
+/* Check if the config_options and the corresponding enum have compatible
+ * sizes. */
+mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
+                     == (NUM_OPTIONS + 1),
+                 "config_options and enum not sync");
+
+enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
+
+struct mg_handler_info {
+       /* Name/Pattern of the URI. */
+       char *uri;
+       size_t uri_len;
+
+       /* handler type */
+       int handler_type;
+
+       /* Handler for http/https or authorization requests. */
+       mg_request_handler handler;
+
+       /* Handler for ws/wss (websocket) requests. */
+       mg_websocket_connect_handler connect_handler;
+       mg_websocket_ready_handler ready_handler;
+       mg_websocket_data_handler data_handler;
+       mg_websocket_close_handler close_handler;
+
+       /* Handler for authorization requests */
+       mg_authorization_handler auth_handler;
+
+       /* User supplied argument for the handler function. */
+       void *cbdata;
+
+       /* next handler in a linked list */
+       struct mg_handler_info *next;
+};
+
+struct mg_context {
+       volatile int stop_flag;        /* Should we stop event loop */
+       SSL_CTX *ssl_ctx;              /* SSL context */
+       char *config[NUM_OPTIONS];     /* Civetweb configuration parameters */
+       struct mg_callbacks callbacks; /* User-defined callback function */
+       void *user_data;               /* User-defined data */
+       int context_type;              /* 1 = server context, 2 = client context */
+
+       struct socket *listening_sockets;
+       in_port_t *listening_ports;
+       unsigned int num_listening_sockets;
+
+       volatile int
+           running_worker_threads; /* Number of currently running worker threads */
+       pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+       pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */
+
+       struct socket queue[MGSQLEN]; /* Accepted sockets */
+       volatile int sq_head;         /* Head of the socket queue */
+       volatile int sq_tail;         /* Tail of the socket queue */
+       pthread_cond_t sq_full;       /* Signaled when socket is produced */
+       pthread_cond_t sq_empty;      /* Signaled when socket is consumed */
+       pthread_t masterthreadid;     /* The master thread ID */
+       unsigned int
+           cfg_worker_threads;     /* The number of configured worker threads. */
+       pthread_t *workerthreadids; /* The worker thread IDs */
+
+       time_t start_time;        /* Server start time, used for authentication */
+       uint64_t auth_nonce_mask; /* Mask for all nonce values */
+       pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+       unsigned long nonce_count;   /* Used nonces, used for authentication */
+
+       char *systemName; /* What operating system is running */
+
+       /* linked list of uri handlers */
+       struct mg_handler_info *handlers;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       /* linked list of shared lua websockets */
+       struct mg_shared_lua_websocket_list *shared_lua_websockets;
+#endif
+
+#ifdef USE_TIMERS
+       struct ttimers *timers;
+#endif
+};
+
+
+struct mg_connection {
+       struct mg_request_info request_info;
+       struct mg_context *ctx;
+       SSL *ssl;                 /* SSL descriptor */
+       SSL_CTX *client_ssl_ctx;  /* SSL context for client connections */
+       struct socket client;     /* Connected client */
+       time_t conn_birth_time;   /* Time (wall clock) when connection was
+                                  * established */
+       struct timespec req_time; /* Time (since system start) when the request
+                                  * was received */
+       int64_t num_bytes_sent;   /* Total bytes sent to client */
+       int64_t content_len;      /* Content-Length header value */
+       int64_t consumed_content; /* How many bytes of content have been read */
+       int is_chunked;           /* Transfer-Encoding is chunked: 0=no, 1=yes:
+                                  * data available, 2: all data read */
+       size_t chunk_remainder;   /* Unread data from the last chunk */
+       char *buf;                /* Buffer for received data */
+       char *path_info;          /* PATH_INFO part of the URL */
+
+       int must_close;       /* 1 if connection must be closed */
+       int in_error_handler; /* 1 if in handler for user defined error
+                              * pages */
+       int internal_error;   /* 1 if an error occured while processing the
+                              * request */
+
+       int buf_size;                /* Buffer size */
+       int request_len;             /* Size of the request + headers in a buffer */
+       int data_len;                /* Total size of data in a buffer */
+       int status_code;             /* HTTP reply status code, e.g. 200 */
+       int throttle;                /* Throttling, bytes/sec. <= 0 means no
+                                     * throttle */
+       time_t last_throttle_time;   /* Last time throttled data was sent */
+       int64_t last_throttle_bytes; /* Bytes sent this second */
+       pthread_mutex_t mutex;       /* Used by mg_(un)lock_connection to ensure
+                                     * atomic transmissions for websockets */
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       void *lua_websocket_state; /* Lua_State for a websocket connection */
+#endif
+};
+
+
+static pthread_key_t sTlsKey; /* Thread local storage index */
+static int sTlsInit = 0;
+static int thread_idx_max = 0;
+
+
+struct mg_workerTLS {
+       int is_master;
+       unsigned long thread_idx;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       HANDLE pthread_cond_helper_mutex;
+#endif
+};
+
+/* Directory entry */
+struct de {
+       struct mg_connection *conn;
+       char *file_name;
+       struct file file;
+};
+
+
+#if defined(USE_WEBSOCKET)
+static int is_websocket_protocol(const struct mg_connection *conn);
+#else
+#define is_websocket_protocol(conn) (0)
+#endif
+
+
+static int
+mg_atomic_inc(volatile int *addr)
+{
+       int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       /* Depending on the SDK, this function uses either
+        * (volatile unsigned int *) or (volatile LONG *),
+        * so whatever you use, the other SDK is likely to raise a warning. */
+       ret = InterlockedIncrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        \
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+       ret = __sync_add_and_fetch(addr, 1);
+#else
+       ret = (++(*addr));
+#endif
+       return ret;
+}
+
+
+static int
+mg_atomic_dec(volatile int *addr)
+{
+       int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       /* Depending on the SDK, this function uses either
+        * (volatile unsigned int *) or (volatile LONG *),
+        * so whatever you use, the other SDK is likely to raise a warning. */
+       ret = InterlockedDecrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        \
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+       ret = __sync_sub_and_fetch(addr, 1);
+#else
+       ret = (--(*addr));
+#endif
+       return ret;
+}
+
+#if !defined(NO_THREAD_NAME)
+#if defined(_WIN32) && defined(_MSC_VER)
+/* Set the thread name for debugging purposes in Visual Studio
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+       DWORD dwType;     /* Must be 0x1000. */
+       LPCSTR szName;    /* Pointer to name (in user addr space). */
+       DWORD dwThreadID; /* Thread ID (-1=caller thread). */
+       DWORD dwFlags;    /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+#elif defined(__linux__)
+#include <sys/prctl.h>
+#include <sys/sendfile.h>
+#endif
+
+
+static void
+mg_set_thread_name(const char *name)
+{
+       char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
+
+       mg_snprintf(
+           NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
+
+#if defined(_WIN32)
+#if defined(_MSC_VER)
+       /* Windows and Visual Studio Compiler */
+       __try
+       {
+               THREADNAME_INFO info;
+               info.dwType = 0x1000;
+               info.szName = threadName;
+               info.dwThreadID = ~0U;
+               info.dwFlags = 0;
+
+               RaiseException(0x406D1388,
+                              0,
+                              sizeof(info) / sizeof(ULONG_PTR),
+                              (ULONG_PTR *)&info);
+       }
+       __except(EXCEPTION_EXECUTE_HANDLER)
+       {
+       }
+#elif defined(__MINGW32__)
+/* No option known to set thread name for MinGW */
+#endif
+#elif defined(__GLIBC__)                                                       \
+    && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
+       /* pthread_setname_np first appeared in glibc in version 2.12*/
+       (void)pthread_setname_np(pthread_self(), threadName);
+#elif defined(__linux__)
+       /* on linux we can use the old prctl function */
+       (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
+#endif
+}
+#else /* !defined(NO_THREAD_NAME) */
+void
+mg_set_thread_name(const char *threadName)
+{
+}
+#endif
+
+
+#if defined(MG_LEGACY_INTERFACE)
+const char **
+mg_get_valid_option_names(void)
+{
+       /* This function is deprecated. Use mg_get_valid_options instead. */
+       static const char *
+           data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0};
+       int i;
+
+       for (i = 0; config_options[i].name != NULL; i++) {
+               data[i * 2] = config_options[i].name;
+               data[i * 2 + 1] = config_options[i].default_value;
+       }
+
+       return data;
+}
+#endif
+
+
+const struct mg_option *
+mg_get_valid_options(void)
+{
+       return config_options;
+}
+
+
+static int
+is_file_in_memory(const struct mg_connection *conn,
+                  const char *path,
+                  struct file *filep)
+{
+       size_t size = 0;
+       if (!conn || !filep) {
+               return 0;
+       }
+
+       if (conn->ctx->callbacks.open_file) {
+               filep->membuf = conn->ctx->callbacks.open_file(conn, path, &size);
+               if (filep->membuf != NULL) {
+                       /* NOTE: override filep->size only on success. Otherwise, it might
+                        * break constructs like if (!mg_stat() || !mg_fopen()) ... */
+                       filep->size = size;
+               }
+       }
+
+       return filep->membuf != NULL;
+}
+
+
+static int
+is_file_opened(const struct file *filep)
+{
+       if (!filep) {
+               return 0;
+       }
+
+       return filep->membuf != NULL || filep->fp != NULL;
+}
+
+
+/* mg_fopen will open a file either in memory or on the disk.
+ * The input parameter path is a string in UTF-8 encoding.
+ * The input parameter mode is the same as for fopen.
+ * Either fp or membuf will be set in the output struct filep.
+ * The function returns 1 on success, 0 on error. */
+static int
+mg_fopen(const struct mg_connection *conn,
+         const char *path,
+         const char *mode,
+         struct file *filep)
+{
+       struct stat st;
+
+       if (!filep) {
+               return 0;
+       }
+
+       /* TODO (high): mg_fopen should only open a file, while mg_stat should
+        * only get the file status. They should not work on different members of
+        * the same structure (bad cohesion). */
+       memset(filep, 0, sizeof(*filep));
+
+       if (stat(path, &st) == 0) {
+               filep->size = (uint64_t)(st.st_size);
+       }
+
+       if (!is_file_in_memory(conn, path, filep)) {
+#ifdef _WIN32
+               wchar_t wbuf[PATH_MAX], wmode[20];
+               path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+               MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
+               filep->fp = _wfopen(wbuf, wmode);
+#else
+               /* Linux et al already use unicode. No need to convert. */
+               filep->fp = fopen(path, mode);
+#endif
+       }
+
+       return is_file_opened(filep);
+}
+
+
+static void
+mg_fclose(struct file *filep)
+{
+       if (filep != NULL && filep->fp != NULL) {
+               fclose(filep->fp);
+       }
+}
+
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+       for (; *src != '\0' && n > 1; n--) {
+               *dst++ = *src++;
+       }
+       *dst = '\0';
+}
+
+
+static int
+lowercase(const char *s)
+{
+       return tolower(*(const unsigned char *)s);
+}
+
+
+int
+mg_strncasecmp(const char *s1, const char *s2, size_t len)
+{
+       int diff = 0;
+
+       if (len > 0) {
+               do {
+                       diff = lowercase(s1++) - lowercase(s2++);
+               } while (diff == 0 && s1[-1] != '\0' && --len > 0);
+       }
+
+       return diff;
+}
+
+
+int
+mg_strcasecmp(const char *s1, const char *s2)
+{
+       int diff;
+
+       do {
+               diff = lowercase(s1++) - lowercase(s2++);
+       } while (diff == 0 && s1[-1] != '\0');
+
+       return diff;
+}
+
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+       char *p;
+
+       if ((p = (char *)mg_malloc(len + 1)) != NULL) {
+               mg_strlcpy(p, ptr, len + 1);
+       }
+
+       return p;
+}
+
+
+static char *
+mg_strdup(const char *str)
+{
+       return mg_strndup(str, strlen(str));
+}
+
+
+static const char *
+mg_strcasestr(const char *big_str, const char *small_str)
+{
+       size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
+
+       if (big_len >= small_len) {
+               for (i = 0; i <= (big_len - small_len); i++) {
+                       if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
+                               return big_str + i;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+/* Return null terminated string of given maximum length.
+ * Report errors if length is exceeded. */
+static void
+mg_vsnprintf(const struct mg_connection *conn,
+             int *truncated,
+             char *buf,
+             size_t buflen,
+             const char *fmt,
+             va_list ap)
+{
+       int n, ok;
+
+       if (buflen == 0) {
+               return;
+       }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+/* Using fmt as a non-literal is intended here, since it is mostly called
+ * indirectly by mg_snprintf */
+#endif
+
+       n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
+       ok = (n >= 0) && ((size_t)n < buflen);
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+       if (ok) {
+               if (truncated) {
+                       *truncated = 0;
+               }
+       } else {
+               if (truncated) {
+                       *truncated = 1;
+               }
+               mg_cry(conn,
+                      "truncating vsnprintf buffer: [%.*s]",
+                      (int)((buflen > 200) ? 200 : (buflen - 1)),
+                      buf);
+               n = (int)buflen - 1;
+       }
+       buf[n] = '\0';
+}
+
+
+static void
+mg_snprintf(const struct mg_connection *conn,
+            int *truncated,
+            char *buf,
+            size_t buflen,
+            const char *fmt,
+            ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
+       va_end(ap);
+}
+
+
+static int
+get_option_index(const char *name)
+{
+       int i;
+
+       for (i = 0; config_options[i].name != NULL; i++) {
+               if (strcmp(config_options[i].name, name) == 0) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+
+const char *
+mg_get_option(const struct mg_context *ctx, const char *name)
+{
+       int i;
+       if ((i = get_option_index(name)) == -1) {
+               return NULL;
+       } else if (!ctx || ctx->config[i] == NULL) {
+               return "";
+       } else {
+               return ctx->config[i];
+       }
+}
+
+
+struct mg_context *
+mg_get_context(const struct mg_connection *conn)
+{
+       return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx);
+}
+
+
+void *
+mg_get_user_data(const struct mg_context *ctx)
+{
+       return (ctx == NULL) ? NULL : ctx->user_data;
+}
+
+
+void
+mg_set_user_connection_data(struct mg_connection *conn, void *data)
+{
+       if (conn != NULL) {
+               conn->request_info.conn_data = data;
+       }
+}
+
+
+void *
+mg_get_user_connection_data(const struct mg_connection *conn)
+{
+       if (conn != NULL) {
+               return conn->request_info.conn_data;
+       }
+       return NULL;
+}
+
+
+size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
+{
+       size_t i;
+       if (!ctx) {
+               return 0;
+       }
+       for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
+               ssl[i] = ctx->listening_sockets[i].is_ssl;
+               ports[i] = ctx->listening_ports[i];
+       }
+       return i;
+}
+
+
+int
+mg_get_server_ports(const struct mg_context *ctx,
+                    int size,
+                    struct mg_server_ports *ports)
+{
+       int i, cnt = 0;
+
+       if (size <= 0) {
+               return -1;
+       }
+       memset(ports, 0, sizeof(*ports) * (size_t)size);
+       if (!ctx) {
+               return -1;
+       }
+       if (!ctx->listening_sockets || !ctx->listening_ports) {
+               return -1;
+       }
+
+       for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
+
+               ports[cnt].port = ctx->listening_ports[i];
+               ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
+               ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
+
+               if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
+                       /* IPv4 */
+                       ports[cnt].protocol = 1;
+                       cnt++;
+               } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
+                       /* IPv6 */
+                       ports[cnt].protocol = 3;
+                       cnt++;
+               }
+       }
+
+       return cnt;
+}
+
+
+static void
+sockaddr_to_string(char *buf, size_t len, const union usa *usa)
+{
+       buf[0] = '\0';
+
+       if (!usa) {
+               return;
+       }
+
+       if (usa->sa.sa_family == AF_INET) {
+               getnameinfo(&usa->sa,
+                           sizeof(usa->sin),
+                           buf,
+                           (unsigned)len,
+                           NULL,
+                           0,
+                           NI_NUMERICHOST);
+       }
+#if defined(USE_IPV6)
+       else if (usa->sa.sa_family == AF_INET6) {
+               getnameinfo(&usa->sa,
+                           sizeof(usa->sin6),
+                           buf,
+                           (unsigned)len,
+                           NULL,
+                           0,
+                           NI_NUMERICHOST);
+       }
+#endif
+}
+
+
+/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
+ * included in all responses other than 100, 101, 5xx. */
+static void
+gmt_time_string(char *buf, size_t buf_len, time_t *t)
+{
+       struct tm *tm;
+
+       tm = ((t != NULL) ? gmtime(t) : NULL);
+       if (tm != NULL) {
+               strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
+       } else {
+               mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
+               buf[buf_len - 1] = '\0';
+       }
+}
+
+
+/* difftime for struct timespec. Return value is in seconds. */
+static double
+mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
+{
+       return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
+              + (double)(ts_now->tv_sec - ts_before->tv_sec);
+}
+
+
+/* Print error message to the opened error log stream. */
+void
+mg_cry(const struct mg_connection *conn, const char *fmt, ...)
+{
+       char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
+       va_list ap;
+       struct file fi;
+       time_t timestamp;
+
+       va_start(ap, fmt);
+       IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
+       va_end(ap);
+       buf[sizeof(buf) - 1] = 0;
+
+       if (!conn) {
+               puts(buf);
+               return;
+       }
+
+       /* Do not lock when getting the callback value, here and below.
+        * I suppose this is fine, since function cannot disappear in the
+        * same way string option can. */
+       if ((conn->ctx->callbacks.log_message == NULL)
+           || (conn->ctx->callbacks.log_message(conn, buf) == 0)) {
+
+               if (conn->ctx->config[ERROR_LOG_FILE] != NULL) {
+                       if (mg_fopen(conn, conn->ctx->config[ERROR_LOG_FILE], "a+", &fi)
+                           == 0) {
+                               fi.fp = NULL;
+                       }
+               } else {
+                       fi.fp = NULL;
+               }
+
+               if (fi.fp != NULL) {
+                       flockfile(fi.fp);
+                       timestamp = time(NULL);
+
+                       sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+                       fprintf(fi.fp,
+                               "[%010lu] [error] [client %s] ",
+                               (unsigned long)timestamp,
+                               src_addr);
+
+                       if (conn->request_info.request_method != NULL) {
+                               fprintf(fi.fp,
+                                       "%s %s: ",
+                                       conn->request_info.request_method,
+                                       conn->request_info.request_uri);
+                       }
+
+                       fprintf(fi.fp, "%s", buf);
+                       fputc('\n', fi.fp);
+                       fflush(fi.fp);
+                       funlockfile(fi.fp);
+                       mg_fclose(&fi);
+               }
+       }
+}
+
+
+/* Return fake connection structure. Used for logging, if connection
+ * is not applicable at the moment of logging. */
+static struct mg_connection *
+fc(struct mg_context *ctx)
+{
+       static struct mg_connection fake_connection;
+       fake_connection.ctx = ctx;
+       return &fake_connection;
+}
+
+
+const char *
+mg_version(void)
+{
+       return CIVETWEB_VERSION;
+}
+
+
+const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *conn)
+{
+       if (!conn) {
+               return NULL;
+       }
+       return &conn->request_info;
+}
+
+
+/* Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the delimiter and following whitespaces.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ * Delimiters can be quoted with quotechar. */
+static char *
+skip_quoted(char **buf,
+            const char *delimiters,
+            const char *whitespace,
+            char quotechar)
+{
+       char *p, *begin_word, *end_word, *end_whitespace;
+
+       begin_word = *buf;
+       end_word = begin_word + strcspn(begin_word, delimiters);
+
+       /* Check for quotechar */
+       if (end_word > begin_word) {
+               p = end_word - 1;
+               while (*p == quotechar) {
+                       /* While the delimiter is quoted, look for the next delimiter. */
+                       /* This happens, e.g., in calls from parse_auth_header,
+                        * if the user name contains a " character. */
+
+                       /* If there is anything beyond end_word, copy it. */
+                       if (*end_word != '\0') {
+                               size_t end_off = strcspn(end_word + 1, delimiters);
+                               memmove(p, end_word, end_off + 1);
+                               p += end_off; /* p must correspond to end_word - 1 */
+                               end_word += end_off + 1;
+                       } else {
+                               *p = '\0';
+                               break;
+                       }
+               }
+               for (p++; p < end_word; p++) {
+                       *p = '\0';
+               }
+       }
+
+       if (*end_word == '\0') {
+               *buf = end_word;
+       } else {
+               end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
+
+               for (p = end_word; p < end_whitespace; p++) {
+                       *p = '\0';
+               }
+
+               *buf = end_whitespace;
+       }
+
+       return begin_word;
+}
+
+
+/* Simplified version of skip_quoted without quote char
+ * and whitespace == delimiters */
+static char *
+skip(char **buf, const char *delimiters)
+{
+       return skip_quoted(buf, delimiters, delimiters, 0);
+}
+
+
+/* Return HTTP header value, or NULL if not found. */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+       int i;
+       if (ri) {
+               for (i = 0; i < ri->num_headers; i++) {
+                       if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
+                               return ri->http_headers[i].value;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+       if (!conn) {
+               return NULL;
+       }
+
+       return get_header(&conn->request_info, name);
+}
+
+
+/* A helper function for traversing a comma separated list of values.
+ * It returns a list pointer shifted to the next value, or NULL if the end
+ * of the list found.
+ * Value is stored in val vector. If value has form "x=y", then eq_val
+ * vector is initialized to point to the "y" part, and val vector length
+ * is adjusted to point only to "x". */
+static const char *
+next_option(const char *list, struct vec *val, struct vec *eq_val)
+{
+       int end;
+
+reparse:
+       if (val == NULL || list == NULL || *list == '\0') {
+               /* End of the list */
+               list = NULL;
+       } else {
+               /* Skip over leading LWS */
+               while (*list == ' ' || *list == '\t')
+                       list++;
+
+               val->ptr = list;
+               if ((list = strchr(val->ptr, ',')) != NULL) {
+                       /* Comma found. Store length and shift the list ptr */
+                       val->len = ((size_t)(list - val->ptr));
+                       list++;
+               } else {
+                       /* This value is the last one */
+                       list = val->ptr + strlen(val->ptr);
+                       val->len = ((size_t)(list - val->ptr));
+               }
+
+               /* Adjust length for trailing LWS */
+               end = (int)val->len - 1;
+               while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t'))
+                       end--;
+               val->len = (size_t)(end + 1);
+
+               if (val->len == 0) {
+                       /* Ignore any empty entries. */
+                       goto reparse;
+               }
+
+               if (eq_val != NULL) {
+                       /* Value has form "x=y", adjust pointers and lengths
+                        * so that val points to "x", and eq_val points to "y". */
+                       eq_val->len = 0;
+                       eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
+                       if (eq_val->ptr != NULL) {
+                               eq_val->ptr++; /* Skip over '=' character */
+                               eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
+                               val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
+                       }
+               }
+       }
+
+       return list;
+}
+
+/* A helper function for checking if a comma separated list of values contains
+ * the given option (case insensitvely).
+ * 'header' can be NULL, in which case false is returned. */
+static int
+header_has_option(const char *header, const char *option)
+{
+       struct vec opt_vec;
+       struct vec eq_vec;
+
+       assert(option != NULL);
+       assert(option[0] != '\0');
+
+       while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
+               if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Perform case-insensitive match of string against pattern */
+static int
+match_prefix(const char *pattern, size_t pattern_len, const char *str)
+{
+       const char *or_str;
+       size_t i;
+       int j, len, res;
+
+       if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) {
+               res = match_prefix(pattern, (size_t)(or_str - pattern), str);
+               return res > 0 ? res : match_prefix(or_str + 1,
+                                                   (size_t)((pattern + pattern_len)
+                                                            - (or_str + 1)),
+                                                   str);
+       }
+
+       for (i = 0, j = 0; i < pattern_len; i++, j++) {
+               if (pattern[i] == '?' && str[j] != '\0') {
+                       continue;
+               } else if (pattern[i] == '$') {
+                       return str[j] == '\0' ? j : -1;
+               } else if (pattern[i] == '*') {
+                       i++;
+                       if (pattern[i] == '*') {
+                               i++;
+                               len = (int)strlen(str + j);
+                       } else {
+                               len = (int)strcspn(str + j, "/");
+                       }
+                       if (i == pattern_len) {
+                               return j + len;
+                       }
+                       do {
+                               res = match_prefix(pattern + i, pattern_len - i, str + j + len);
+                       } while (res == -1 && len-- > 0);
+                       return res == -1 ? -1 : j + res + len;
+               } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
+                       return -1;
+               }
+       }
+       return j;
+}
+
+
+/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
+ * This function must tolerate situations when connection info is not
+ * set up, for example if request parsing failed. */
+static int
+should_keep_alive(const struct mg_connection *conn)
+{
+       if (conn != NULL) {
+               const char *http_version = conn->request_info.http_version;
+               const char *header = mg_get_header(conn, "Connection");
+               if (conn->must_close || conn->internal_error || conn->status_code == 401
+                   || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0
+                   || (header != NULL && !header_has_option(header, "keep-alive"))
+                   || (header == NULL && http_version
+                       && 0 != strcmp(http_version, "1.1"))) {
+                       return 0;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+
+static int
+should_decode_url(const struct mg_connection *conn)
+{
+       if (!conn || !conn->ctx) {
+               return 0;
+       }
+
+       return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0);
+}
+
+
+static const char *
+suggest_connection_header(const struct mg_connection *conn)
+{
+       return should_keep_alive(conn) ? "keep-alive" : "close";
+}
+
+
+static int
+send_no_cache_header(struct mg_connection *conn)
+{
+       /* Send all current and obsolete cache opt-out directives. */
+       return mg_printf(conn,
+                        "Cache-Control: no-cache, no-store, "
+                        "must-revalidate, private, max-age=0\r\n"
+                        "Pragma: no-cache\r\n"
+                        "Expires: 0\r\n");
+}
+
+
+static int
+send_static_cache_header(struct mg_connection *conn)
+{
+#if !defined(NO_CACHING)
+       /* Read the server config to check how long a file may be cached.
+        * The configuration is in seconds. */
+       int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]);
+       if (max_age <= 0) {
+               /* 0 means "do not cache". All values <0 are reserved
+                * and may be used differently in the future. */
+               /* If a file should not be cached, do not only send
+                * max-age=0, but also pragmas and Expires headers. */
+               return send_no_cache_header(conn);
+       }
+
+       /* Use "Cache-Control: max-age" instead of "Expires" header.
+        * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
+       /* See also https://www.mnot.net/cache_docs/ */
+       /* According to RFC 2616, Section 14.21, caching times should not exceed
+        * one year. A year with 365 days corresponds to 31536000 seconds, a leap
+        * year to 31622400 seconds. For the moment, we just send whatever has
+        * been configured, still the behavior for >1 year should be considered
+        * as undefined. */
+       return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age);
+#else  /* NO_CACHING */
+       return send_no_cache_header(conn);
+#endif /* !NO_CACHING */
+}
+
+
+static void handle_file_based_request(struct mg_connection *conn,
+                                      const char *path,
+                                      struct file *filep);
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep);
+
+
+const char *
+mg_get_response_code_text(struct mg_connection *conn, int response_code)
+{
+       /* See IANA HTTP status code assignment:
+        * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+        */
+
+       switch (response_code) {
+       /* RFC2616 Section 10.1 - Informational 1xx */
+       case 100:
+               return "Continue"; /* RFC2616 Section 10.1.1 */
+       case 101:
+               return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
+       case 102:
+               return "Processing"; /* RFC2518 Section 10.1 */
+
+       /* RFC2616 Section 10.2 - Successful 2xx */
+       case 200:
+               return "OK"; /* RFC2616 Section 10.2.1 */
+       case 201:
+               return "Created"; /* RFC2616 Section 10.2.2 */
+       case 202:
+               return "Accepted"; /* RFC2616 Section 10.2.3 */
+       case 203:
+               return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
+       case 204:
+               return "No Content"; /* RFC2616 Section 10.2.5 */
+       case 205:
+               return "Reset Content"; /* RFC2616 Section 10.2.6 */
+       case 206:
+               return "Partial Content"; /* RFC2616 Section 10.2.7 */
+       case 207:
+               return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */
+       case 208:
+               return "Already Reported"; /* RFC5842 Section 7.1 */
+
+       case 226:
+               return "IM used"; /* RFC3229 Section 10.4.1 */
+
+       /* RFC2616 Section 10.3 - Redirection 3xx */
+       case 300:
+               return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
+       case 301:
+               return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
+       case 302:
+               return "Found"; /* RFC2616 Section 10.3.3 */
+       case 303:
+               return "See Other"; /* RFC2616 Section 10.3.4 */
+       case 304:
+               return "Not Modified"; /* RFC2616 Section 10.3.5 */
+       case 305:
+               return "Use Proxy"; /* RFC2616 Section 10.3.6 */
+       case 307:
+               return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
+       case 308:
+               return "Permanent Redirect"; /* RFC7238 Section 3 */
+
+       /* RFC2616 Section 10.4 - Client Error 4xx */
+       case 400:
+               return "Bad Request"; /* RFC2616 Section 10.4.1 */
+       case 401:
+               return "Unauthorized"; /* RFC2616 Section 10.4.2 */
+       case 402:
+               return "Payment Required"; /* RFC2616 Section 10.4.3 */
+       case 403:
+               return "Forbidden"; /* RFC2616 Section 10.4.4 */
+       case 404:
+               return "Not Found"; /* RFC2616 Section 10.4.5 */
+       case 405:
+               return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
+       case 406:
+               return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
+       case 407:
+               return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
+       case 408:
+               return "Request Time-out"; /* RFC2616 Section 10.4.9 */
+       case 409:
+               return "Conflict"; /* RFC2616 Section 10.4.10 */
+       case 410:
+               return "Gone"; /* RFC2616 Section 10.4.11 */
+       case 411:
+               return "Length Required"; /* RFC2616 Section 10.4.12 */
+       case 412:
+               return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
+       case 413:
+               return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
+       case 414:
+               return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
+       case 415:
+               return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
+       case 416:
+               return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */
+       case 417:
+               return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
+
+       case 421:
+               return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
+       case 422:
+               return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
+                                                * Section 11.2 */
+       case 423:
+               return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
+       case 424:
+               return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
+                                            * Section 11.4 */
+
+       case 426:
+               return "Upgrade Required"; /* RFC 2817 Section 4 */
+
+       case 428:
+               return "Precondition Required"; /* RFC 6585, Section 3 */
+       case 429:
+               return "Too Many Requests"; /* RFC 6585, Section 4 */
+
+       case 431:
+               return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
+
+       case 451:
+               return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
+                                                        * Section 3 */
+
+       /* RFC2616 Section 10.5 - Server Error 5xx */
+       case 500:
+               return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
+       case 501:
+               return "Not Implemented"; /* RFC2616 Section 10.5.2 */
+       case 502:
+               return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
+       case 503:
+               return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
+       case 504:
+               return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
+       case 505:
+               return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
+       case 506:
+               return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
+       case 507:
+               return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
+                                               * Section 11.5 */
+       case 508:
+               return "Loop Detected"; /* RFC5842 Section 7.1 */
+
+       case 510:
+               return "Not Extended"; /* RFC 2774, Section 7 */
+       case 511:
+               return "Network Authentication Required"; /* RFC 6585, Section 6 */
+
+       /* Other status codes, not shown in the IANA HTTP status code assignment.
+        * E.g., "de facto" standards due to common use, ... */
+       case 418:
+               return "I am a teapot"; /* RFC2324 Section 2.3.2 */
+       case 419:
+               return "Authentication Timeout"; /* common use */
+       case 420:
+               return "Enhance Your Calm"; /* common use */
+       case 440:
+               return "Login Timeout"; /* common use */
+       case 509:
+               return "Bandwidth Limit Exceeded"; /* common use */
+
+       default:
+               /* This error code is unknown. This should not happen. */
+               if (conn) {
+                       mg_cry(conn, "Unknown HTTP response code: %u", response_code);
+               }
+
+               /* Return at least a category according to RFC 2616 Section 10. */
+               if (response_code >= 100 && response_code < 200) {
+                       /* Unknown informational status code */
+                       return "Information";
+               }
+               if (response_code >= 200 && response_code < 300) {
+                       /* Unknown success code */
+                       return "Success";
+               }
+               if (response_code >= 300 && response_code < 400) {
+                       /* Unknown redirection code */
+                       return "Redirection";
+               }
+               if (response_code >= 400 && response_code < 500) {
+                       /* Unknown request error code */
+                       return "Client Error";
+               }
+               if (response_code >= 500 && response_code < 600) {
+                       /* Unknown server error code */
+                       return "Server Error";
+               }
+
+               /* Response code not even within reasonable range */
+               return "";
+       }
+}
+
+
+static void send_http_error(struct mg_connection *,
+                            int,
+                            PRINTF_FORMAT_STRING(const char *fmt),
+                            ...) PRINTF_ARGS(3, 4);
+
+static void
+send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
+{
+       char buf[MG_BUF_LEN];
+       va_list ap;
+       int len, i, page_handler_found, scope, truncated;
+       char date[64];
+       time_t curtime = time(NULL);
+       const char *error_handler = NULL;
+       struct file error_page_file = STRUCT_FILE_INITIALIZER;
+       const char *error_page_file_ext, *tstr;
+
+       const char *status_text = mg_get_response_code_text(conn, status);
+
+       if (conn == NULL) {
+               return;
+       }
+
+       conn->status_code = status;
+       if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL
+           || conn->ctx->callbacks.http_error(conn, status)) {
+               if (!conn->in_error_handler) {
+                       /* Send user defined error pages, if defined */
+                       error_handler = conn->ctx->config[ERROR_PAGES];
+                       error_page_file_ext = conn->ctx->config[INDEX_FILES];
+                       page_handler_found = 0;
+                       if (error_handler != NULL) {
+                               for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
+                                       switch (scope) {
+                                       case 1: /* Handler for specific error, e.g. 404 error */
+                                               mg_snprintf(conn,
+                                                           &truncated,
+                                                           buf,
+                                                           sizeof(buf) - 32,
+                                                           "%serror%03u.",
+                                                           error_handler,
+                                                           status);
+                                               break;
+                                       case 2: /* Handler for error group, e.g., 5xx error handler
+                                                * for all server errors (500-599) */
+                                               mg_snprintf(conn,
+                                                           &truncated,
+                                                           buf,
+                                                           sizeof(buf) - 32,
+                                                           "%serror%01uxx.",
+                                                           error_handler,
+                                                           status / 100);
+                                               break;
+                                       default: /* Handler for all errors */
+                                               mg_snprintf(conn,
+                                                           &truncated,
+                                                           buf,
+                                                           sizeof(buf) - 32,
+                                                           "%serror.",
+                                                           error_handler);
+                                               break;
+                                       }
+
+                                       /* String truncation in buf may only occur if error_handler
+                                        * is too long. This string is from the config, not from a
+                                        * client. */
+                                       (void)truncated;
+
+                                       len = (int)strlen(buf);
+
+                                       tstr = strchr(error_page_file_ext, '.');
+
+                                       while (tstr) {
+                                               for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ',';
+                                                    i++)
+                                                       buf[len + i - 1] = tstr[i];
+                                               buf[len + i - 1] = 0;
+                                               if (mg_stat(conn, buf, &error_page_file)) {
+                                                       page_handler_found = 1;
+                                                       break;
+                                               }
+                                               tstr = strchr(tstr + i, '.');
+                                       }
+                               }
+                       }
+
+                       if (page_handler_found) {
+                               conn->in_error_handler = 1;
+                               handle_file_based_request(conn, buf, &error_page_file);
+                               conn->in_error_handler = 0;
+                               return;
+                       }
+               }
+
+               /* No custom error page. Send default error page. */
+               gmt_time_string(date, sizeof(date), &curtime);
+
+               conn->must_close = 1;
+               mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
+               send_no_cache_header(conn);
+               mg_printf(conn,
+                         "Date: %s\r\n"
+                         "Connection: close\r\n\r\n",
+                         date);
+
+               /* Errors 1xx, 204 and 304 MUST NOT send a body */
+               if (status > 199 && status != 204 && status != 304) {
+
+                       mg_printf(conn, "Error %d: %s\n", status, status_text);
+
+                       if (fmt != NULL) {
+                               va_start(ap, fmt);
+                               mg_vsnprintf(conn, NULL, buf, sizeof(buf), fmt, ap);
+                               va_end(ap);
+                               mg_write(conn, buf, strlen(buf));
+                               DEBUG_TRACE("Error %i - [%s]", status, buf);
+                       }
+
+               } else {
+                       /* No body allowed. Close the connection. */
+                       DEBUG_TRACE("Error %i", status);
+               }
+       }
+}
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static int
+pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
+{
+       (void)unused;
+       *mutex = CreateMutex(NULL, FALSE, NULL);
+       return *mutex == NULL ? -1 : 0;
+}
+
+
+static int
+pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+       return CloseHandle(*mutex) == 0 ? -1 : 0;
+}
+
+
+static int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+       return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1;
+}
+
+
+#ifdef ENABLE_UNUSED_PTHREAD_FUNCTIONS
+static int
+pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+       switch (WaitForSingleObject(*mutex, 0)) {
+       case WAIT_OBJECT_0:
+               return 0;
+       case WAIT_TIMEOUT:
+               return -2; /* EBUSY */
+       }
+       return -1;
+}
+#endif
+
+
+static int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+       return ReleaseMutex(*mutex) == 0 ? -1 : 0;
+}
+
+
+#ifndef WIN_PTHREADS_TIME_H
+static int
+clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+       FILETIME ft;
+       ULARGE_INTEGER li;
+       BOOL ok = FALSE;
+       double d;
+       static double perfcnt_per_sec = 0.0;
+
+       if (tp) {
+               memset(tp, 0, sizeof(*tp));
+               if (clk_id == CLOCK_REALTIME) {
+                       GetSystemTimeAsFileTime(&ft);
+                       li.LowPart = ft.dwLowDateTime;
+                       li.HighPart = ft.dwHighDateTime;
+                       li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
+                       tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+                       tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+                       ok = TRUE;
+               } else if (clk_id == CLOCK_MONOTONIC) {
+                       if (perfcnt_per_sec == 0.0) {
+                               QueryPerformanceFrequency((LARGE_INTEGER *)&li);
+                               perfcnt_per_sec = 1.0 / li.QuadPart;
+                       }
+                       if (perfcnt_per_sec != 0.0) {
+                               QueryPerformanceCounter((LARGE_INTEGER *)&li);
+                               d = li.QuadPart * perfcnt_per_sec;
+                               tp->tv_sec = (time_t)d;
+                               d -= tp->tv_sec;
+                               tp->tv_nsec = (long)(d * 1.0E9);
+                               ok = TRUE;
+                       }
+               }
+       }
+
+       return ok ? 0 : -1;
+}
+#endif
+
+
+static int
+pthread_cond_init(pthread_cond_t *cv, const void *unused)
+{
+       (void)unused;
+       InitializeCriticalSection(&cv->threadIdSec);
+       cv->waitingthreadcount = 0;
+       cv->waitingthreadhdls =
+           (pthread_t *)mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t));
+       return (cv->waitingthreadhdls != NULL) ? 0 : -1;
+}
+
+
+static int
+pthread_cond_timedwait(pthread_cond_t *cv,
+                       pthread_mutex_t *mutex,
+                       const struct timespec *abstime)
+{
+       struct mg_workerTLS *tls =
+           (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+       int ok;
+       struct timespec tsnow;
+       int64_t nsnow, nswaitabs, nswaitrel;
+       DWORD mswaitrel;
+
+       EnterCriticalSection(&cv->threadIdSec);
+       assert(cv->waitingthreadcount < MAX_WORKER_THREADS);
+       cv->waitingthreadhdls[cv->waitingthreadcount] =
+           tls->pthread_cond_helper_mutex;
+       cv->waitingthreadcount++;
+       LeaveCriticalSection(&cv->threadIdSec);
+
+       if (abstime) {
+               clock_gettime(CLOCK_REALTIME, &tsnow);
+               nsnow = (((int64_t)tsnow.tv_sec) * 1000000000) + tsnow.tv_nsec;
+               nswaitabs =
+                   (((int64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
+               nswaitrel = nswaitabs - nsnow;
+               if (nswaitrel < 0) {
+                       nswaitrel = 0;
+               }
+               mswaitrel = (DWORD)(nswaitrel / 1000000);
+       } else {
+               mswaitrel = INFINITE;
+       }
+
+       pthread_mutex_unlock(mutex);
+       ok = (WAIT_OBJECT_0
+             == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
+       pthread_mutex_lock(mutex);
+
+       return ok ? 0 : -1;
+}
+
+
+static int
+pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
+{
+       return pthread_cond_timedwait(cv, mutex, NULL);
+}
+
+
+static int
+pthread_cond_signal(pthread_cond_t *cv)
+{
+       int i;
+       HANDLE wkup = NULL;
+       BOOL ok = FALSE;
+
+       EnterCriticalSection(&cv->threadIdSec);
+       if (cv->waitingthreadcount) {
+               wkup = cv->waitingthreadhdls[0];
+               ok = SetEvent(wkup);
+
+               for (i = 1; i < cv->waitingthreadcount; i++) {
+                       cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i];
+               }
+               cv->waitingthreadcount--;
+
+               assert(ok);
+       }
+       LeaveCriticalSection(&cv->threadIdSec);
+
+       return ok ? 0 : 1;
+}
+
+
+static int
+pthread_cond_broadcast(pthread_cond_t *cv)
+{
+       EnterCriticalSection(&cv->threadIdSec);
+       while (cv->waitingthreadcount) {
+               pthread_cond_signal(cv);
+       }
+       LeaveCriticalSection(&cv->threadIdSec);
+
+       return 0;
+}
+
+
+static int
+pthread_cond_destroy(pthread_cond_t *cv)
+{
+       EnterCriticalSection(&cv->threadIdSec);
+       assert(cv->waitingthreadcount == 0);
+       mg_free(cv->waitingthreadhdls);
+       cv->waitingthreadhdls = 0;
+       LeaveCriticalSection(&cv->threadIdSec);
+       DeleteCriticalSection(&cv->threadIdSec);
+
+       return 0;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+
+/* For Windows, change all slashes to backslashes in path names. */
+static void
+change_slashes_to_backslashes(char *path)
+{
+       int i;
+
+       for (i = 0; path[i] != '\0'; i++) {
+               if (path[i] == '/') {
+                       path[i] = '\\';
+               }
+
+               /* remove double backslash (check i > 0 to preserve UNC paths,
+                * like \\server\file.txt) */
+               if ((path[i] == '\\') && (i > 0)) {
+                       while (path[i + 1] == '\\' || path[i + 1] == '/') {
+                               (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
+                       }
+               }
+       }
+}
+
+
+static int
+mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
+{
+       int diff;
+
+       do {
+               diff = tolower(*s1) - tolower(*s2);
+               s1++;
+               s2++;
+       } while (diff == 0 && s1[-1] != '\0');
+
+       return diff;
+}
+
+
+/* Encode 'path' which is assumed UTF-8 string, into UNICODE string.
+ * wbuf and wbuf_len is a target buffer and its length. */
+static void
+path_to_unicode(const struct mg_connection *conn,
+                const char *path,
+                wchar_t *wbuf,
+                size_t wbuf_len)
+{
+       char buf[PATH_MAX], buf2[PATH_MAX];
+       wchar_t wbuf2[MAX_PATH + 1];
+       DWORD long_len, err;
+       int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp;
+
+       mg_strlcpy(buf, path, sizeof(buf));
+       change_slashes_to_backslashes(buf);
+
+       /* Convert to Unicode and back. If doubly-converted string does not
+        * match the original, something is fishy, reject. */
+       memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
+       MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
+       WideCharToMultiByte(
+           CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);
+       if (strcmp(buf, buf2) != 0) {
+               wbuf[0] = L'\0';
+       }
+
+       /* TODO: Add a configuration to switch between case sensitive and
+        * case insensitive URIs for Windows server. */
+       /*
+       if (conn) {
+           if (conn->ctx->config[WINDOWS_CASE_SENSITIVE]) {
+               fcompare = wcscmp;
+           }
+       }
+       */
+       (void)conn; /* conn is currently unused */
+
+       /* Only accept a full file path, not a Windows short (8.3) path. */
+       memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t));
+       long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1);
+       if (long_len == 0) {
+               err = GetLastError();
+               if (err == ERROR_FILE_NOT_FOUND) {
+                       /* File does not exist. This is not always a problem here. */
+                       return;
+               }
+       }
+       if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
+               /* Short name is used. */
+               wbuf[0] = L'\0';
+       }
+}
+
+
+#if defined(_WIN32_WCE)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static time_t
+time(time_t *ptime)
+{
+       time_t t;
+       SYSTEMTIME st;
+       FILETIME ft;
+
+       GetSystemTime(&st);
+       SystemTimeToFileTime(&st, &ft);
+       t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+       if (ptime != NULL) {
+               *ptime = t;
+       }
+
+       return t;
+}
+
+
+static struct tm *
+localtime(const time_t *ptime, struct tm *ptm)
+{
+       int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+       FILETIME ft, lft;
+       SYSTEMTIME st;
+       TIME_ZONE_INFORMATION tzinfo;
+
+       if (ptm == NULL) {
+               return NULL;
+       }
+
+       *(int64_t *)&ft = t;
+       FileTimeToLocalFileTime(&ft, &lft);
+       FileTimeToSystemTime(&lft, &st);
+       ptm->tm_year = st.wYear - 1900;
+       ptm->tm_mon = st.wMonth - 1;
+       ptm->tm_wday = st.wDayOfWeek;
+       ptm->tm_mday = st.wDay;
+       ptm->tm_hour = st.wHour;
+       ptm->tm_min = st.wMinute;
+       ptm->tm_sec = st.wSecond;
+       ptm->tm_yday = 0; /* hope nobody uses this */
+       ptm->tm_isdst =
+           GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
+
+       return ptm;
+}
+
+
+static struct tm *
+gmtime(const time_t *ptime, struct tm *ptm)
+{
+       /* FIXME(lsm): fix this. */
+       return localtime(ptime, ptm);
+}
+
+
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+       (void)mg_snprintf(NULL, dst, dst_size, "implement strftime() for WinCE");
+       return 0;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif
+
+
+/* Windows happily opens files with some garbage at the end of file name.
+ * For example, fopen("a.cgi    ", "r") on Windows successfully opens
+ * "a.cgi", despite one would expect an error back.
+ * This function returns non-0 if path ends with some garbage. */
+static int
+path_cannot_disclose_cgi(const char *path)
+{
+       static const char *allowed_last_characters = "_-";
+       int last = path[strlen(path) - 1];
+       return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
+}
+
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
+{
+       wchar_t wbuf[PATH_MAX];
+       WIN32_FILE_ATTRIBUTE_DATA info;
+       time_t creation_time;
+
+       if (!filep) {
+               return 0;
+       }
+       memset(filep, 0, sizeof(*filep));
+
+       if (conn && is_file_in_memory(conn, path, filep)) {
+               /* filep->is_directory = 0; filep->gzipped = 0; .. already done by
+                * memset */
+               filep->last_modified = time(NULL);
+               /* last_modified = now ... assumes the file may change during runtime,
+                * so every mg_fopen call may return different data */
+               /* last_modified = conn->ctx.start_time;
+                * May be used it the data does not change during runtime. This allows
+                * browser caching. Since we do not know, we have to assume the file
+                * in memory may change. */
+               return 1;
+       }
+
+       path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+       if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
+               filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
+               filep->last_modified =
+                   SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
+                                 info.ftLastWriteTime.dwHighDateTime);
+
+               /* On Windows, the file creation time can be higher than the
+                * modification time, e.g. when a file is copied.
+                * Since the Last-Modified timestamp is used for caching
+                * it should be based on the most recent timestamp. */
+               creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
+                                             info.ftCreationTime.dwHighDateTime);
+               if (creation_time > filep->last_modified) {
+                       filep->last_modified = creation_time;
+               }
+
+               filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+               /* If file name is fishy, reset the file structure and return
+                * error.
+                * Note it is important to reset, not just return the error, cause
+                * functions like is_file_opened() check the struct. */
+               if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
+                       memset(filep, 0, sizeof(*filep));
+                       return 0;
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static int
+mg_remove(const struct mg_connection *conn, const char *path)
+{
+       wchar_t wbuf[PATH_MAX];
+       path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+       return DeleteFileW(wbuf) ? 0 : -1;
+}
+
+
+static int
+mg_mkdir(const struct mg_connection *conn, const char *path, int mode)
+{
+       wchar_t wbuf[PATH_MAX];
+       (void)mode;
+       path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+       return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
+}
+
+
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+/* Implementation of POSIX opendir/closedir/readdir for Windows. */
+static DIR *
+mg_opendir(const struct mg_connection *conn, const char *name)
+{
+       DIR *dir = NULL;
+       wchar_t wpath[PATH_MAX];
+       DWORD attrs;
+
+       if (name == NULL) {
+               SetLastError(ERROR_BAD_ARGUMENTS);
+       } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) {
+               SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+       } else {
+               path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath));
+               attrs = GetFileAttributesW(wpath);
+               if (attrs != 0xFFFFFFFF && ((attrs & FILE_ATTRIBUTE_DIRECTORY)
+                                           == FILE_ATTRIBUTE_DIRECTORY)) {
+                       (void)wcscat(wpath, L"\\*");
+                       dir->handle = FindFirstFileW(wpath, &dir->info);
+                       dir->result.d_name[0] = '\0';
+               } else {
+                       mg_free(dir);
+                       dir = NULL;
+               }
+       }
+
+       return dir;
+}
+
+
+static int
+mg_closedir(DIR *dir)
+{
+       int result = 0;
+
+       if (dir != NULL) {
+               if (dir->handle != INVALID_HANDLE_VALUE)
+                       result = FindClose(dir->handle) ? 0 : -1;
+
+               mg_free(dir);
+       } else {
+               result = -1;
+               SetLastError(ERROR_BAD_ARGUMENTS);
+       }
+
+       return result;
+}
+
+
+static struct dirent *
+mg_readdir(DIR *dir)
+{
+       struct dirent *result = 0;
+
+       if (dir) {
+               if (dir->handle != INVALID_HANDLE_VALUE) {
+                       result = &dir->result;
+                       (void)WideCharToMultiByte(CP_UTF8,
+                                                 0,
+                                                 dir->info.cFileName,
+                                                 -1,
+                                                 result->d_name,
+                                                 sizeof(result->d_name),
+                                                 NULL,
+                                                 NULL);
+
+                       if (!FindNextFileW(dir->handle, &dir->info)) {
+                               (void)FindClose(dir->handle);
+                               dir->handle = INVALID_HANDLE_VALUE;
+                       }
+
+               } else {
+                       SetLastError(ERROR_FILE_NOT_FOUND);
+               }
+       } else {
+               SetLastError(ERROR_BAD_ARGUMENTS);
+       }
+
+       return result;
+}
+
+
+#ifndef HAVE_POLL
+static int
+poll(struct pollfd *pfd, unsigned int n, int milliseconds)
+{
+       struct timeval tv;
+       fd_set set;
+       unsigned int i;
+       int result;
+       SOCKET maxfd = 0;
+
+       memset(&tv, 0, sizeof(tv));
+       tv.tv_sec = milliseconds / 1000;
+       tv.tv_usec = (milliseconds % 1000) * 1000;
+       FD_ZERO(&set);
+
+       for (i = 0; i < n; i++) {
+               FD_SET((SOCKET)pfd[i].fd, &set);
+               pfd[i].revents = 0;
+
+               if (pfd[i].fd > maxfd) {
+                       maxfd = pfd[i].fd;
+               }
+       }
+
+       if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
+               for (i = 0; i < n; i++) {
+                       if (FD_ISSET(pfd[i].fd, &set)) {
+                               pfd[i].revents = POLLIN;
+                       }
+               }
+       }
+
+       return result;
+}
+#endif /* HAVE_POLL */
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+
+static void
+set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */)
+{
+       (void)conn; /* Unused. */
+       (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
+}
+
+
+int
+mg_start_thread(mg_thread_func_t f, void *p)
+{
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+       /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
+        */
+       return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p)
+                == ((uintptr_t)(-1L)))
+                   ? -1
+                   : 0);
+#else
+       return (
+           (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L)))
+               ? -1
+               : 0);
+#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
+}
+
+
+/* Start a thread storing the thread context. */
+static int
+mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
+                        void *p,
+                        pthread_t *threadidptr)
+{
+       uintptr_t uip;
+       HANDLE threadhandle;
+       int result = -1;
+
+       uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL);
+       threadhandle = (HANDLE)uip;
+       if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) {
+               *threadidptr = threadhandle;
+               result = 0;
+       }
+
+       return result;
+}
+
+
+/* Wait for a thread to finish. */
+static int
+mg_join_thread(pthread_t threadid)
+{
+       int result;
+       DWORD dwevent;
+
+       result = -1;
+       dwevent = WaitForSingleObject(threadid, INFINITE);
+       if (dwevent == WAIT_FAILED) {
+               DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO);
+       } else {
+               if (dwevent == WAIT_OBJECT_0) {
+                       CloseHandle(threadid);
+                       result = 0;
+               }
+       }
+
+       return result;
+}
+
+#if !defined(NO_SSL_DL)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static HANDLE
+dlopen(const char *dll_name, int flags)
+{
+       wchar_t wbuf[PATH_MAX];
+       (void)flags;
+       path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf));
+       return LoadLibraryW(wbuf);
+}
+
+
+static int
+dlclose(void *handle)
+{
+       int result;
+
+       if (FreeLibrary((HMODULE)handle) != 0) {
+               result = 0;
+       } else {
+               result = -1;
+       }
+
+       return result;
+}
+
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif
+
+
+#if !defined(NO_CGI)
+#define SIGKILL (0)
+
+static int
+kill(pid_t pid, int sig_num)
+{
+       (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
+       (void)CloseHandle((HANDLE)pid);
+       return 0;
+}
+
+
+static void
+trim_trailing_whitespaces(char *s)
+{
+       char *e = s + strlen(s) - 1;
+       while (e > s && isspace(*(unsigned char *)e)) {
+               *e-- = '\0';
+       }
+}
+
+
+static pid_t
+spawn_process(struct mg_connection *conn,
+              const char *prog,
+              char *envblk,
+              char *envp[],
+              int fdin[2],
+              int fdout[2],
+              int fderr[2],
+              const char *dir)
+{
+       HANDLE me;
+       char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
+           cmdline[PATH_MAX], buf[PATH_MAX];
+       int truncated;
+       struct file file = STRUCT_FILE_INITIALIZER;
+       STARTUPINFOA si;
+       PROCESS_INFORMATION pi = {0};
+
+       (void)envp;
+
+       memset(&si, 0, sizeof(si));
+       si.cb = sizeof(si);
+
+       si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+       si.wShowWindow = SW_HIDE;
+
+       me = GetCurrentProcess();
+       DuplicateHandle(me,
+                       (HANDLE)_get_osfhandle(fdin[0]),
+                       me,
+                       &si.hStdInput,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS);
+       DuplicateHandle(me,
+                       (HANDLE)_get_osfhandle(fdout[1]),
+                       me,
+                       &si.hStdOutput,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS);
+       DuplicateHandle(me,
+                       (HANDLE)_get_osfhandle(fderr[1]),
+                       me,
+                       &si.hStdError,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS);
+
+       /* Mark handles that should not be inherited. See
+        * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
+        */
+       SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]),
+                            HANDLE_FLAG_INHERIT,
+                            0);
+       SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]),
+                            HANDLE_FLAG_INHERIT,
+                            0);
+       SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]),
+                            HANDLE_FLAG_INHERIT,
+                            0);
+
+       /* If CGI file is a script, try to read the interpreter line */
+       interp = conn->ctx->config[CGI_INTERPRETER];
+       if (interp == NULL) {
+               buf[0] = buf[1] = '\0';
+
+               /* Read the first line of the script into the buffer */
+               mg_snprintf(
+                   conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
+
+               if (truncated) {
+                       pi.hProcess = (pid_t)-1;
+                       goto spawn_cleanup;
+               }
+
+               if (mg_fopen(conn, cmdline, "r", &file)) {
+                       p = (char *)file.membuf;
+                       mg_fgets(buf, sizeof(buf), &file, &p);
+                       mg_fclose(&file);
+                       buf[sizeof(buf) - 1] = '\0';
+               }
+
+               if (buf[0] == '#' && buf[1] == '!') {
+                       trim_trailing_whitespaces(buf + 2);
+               } else {
+                       buf[2] = '\0';
+               }
+               interp = buf + 2;
+       }
+
+       if (interp[0] != '\0') {
+               GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
+               interp = full_interp;
+       }
+       GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
+
+       if (interp[0] != '\0') {
+               mg_snprintf(conn,
+                           &truncated,
+                           cmdline,
+                           sizeof(cmdline),
+                           "\"%s\" \"%s\\%s\"",
+                           interp,
+                           full_dir,
+                           prog);
+       } else {
+               mg_snprintf(conn,
+                           &truncated,
+                           cmdline,
+                           sizeof(cmdline),
+                           "\"%s\\%s\"",
+                           full_dir,
+                           prog);
+       }
+
+       if (truncated) {
+               pi.hProcess = (pid_t)-1;
+               goto spawn_cleanup;
+       }
+
+       DEBUG_TRACE("Running [%s]", cmdline);
+       if (CreateProcessA(NULL,
+                          cmdline,
+                          NULL,
+                          NULL,
+                          TRUE,
+                          CREATE_NEW_PROCESS_GROUP,
+                          envblk,
+                          NULL,
+                          &si,
+                          &pi) == 0) {
+               mg_cry(
+                   conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO);
+               pi.hProcess = (pid_t)-1;
+               /* goto spawn_cleanup; */
+       }
+
+spawn_cleanup:
+       (void)CloseHandle(si.hStdOutput);
+       (void)CloseHandle(si.hStdError);
+       (void)CloseHandle(si.hStdInput);
+       if (pi.hThread != NULL) {
+               (void)CloseHandle(pi.hThread);
+       }
+
+       return (pid_t)pi.hProcess;
+}
+#endif /* !NO_CGI */
+
+
+static int
+set_non_blocking_mode(SOCKET sock)
+{
+       unsigned long on = 1;
+       return ioctlsocket(sock, (long)FIONBIO, &on);
+}
+
+#else
+
+static int
+mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
+{
+       struct stat st;
+       if (!filep) {
+               return 0;
+       }
+       memset(filep, 0, sizeof(*filep));
+
+       if (conn && is_file_in_memory(conn, path, filep)) {
+               return 1;
+       }
+
+       if (0 == stat(path, &st)) {
+               filep->size = (uint64_t)(st.st_size);
+               filep->last_modified = st.st_mtime;
+               filep->is_directory = S_ISDIR(st.st_mode);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void
+set_close_on_exec(SOCKET fd, struct mg_connection *conn /* may be null */)
+{
+       if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+               if (conn) {
+                       mg_cry(conn,
+                              "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
+                              __func__,
+                              strerror(ERRNO));
+               }
+       }
+}
+
+
+int
+mg_start_thread(mg_thread_func_t func, void *param)
+{
+       pthread_t thread_id;
+       pthread_attr_t attr;
+       int result;
+
+       (void)pthread_attr_init(&attr);
+       (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+       /* Compile-time option to control stack size,
+        * e.g. -DUSE_STACK_SIZE=16384 */
+       (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
+#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
+
+       result = pthread_create(&thread_id, &attr, func, param);
+       pthread_attr_destroy(&attr);
+
+       return result;
+}
+
+
+/* Start a thread storing the thread context. */
+static int
+mg_start_thread_with_id(mg_thread_func_t func,
+                        void *param,
+                        pthread_t *threadidptr)
+{
+       pthread_t thread_id;
+       pthread_attr_t attr;
+       int result;
+
+       (void)pthread_attr_init(&attr);
+
+#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
+       /* Compile-time option to control stack size,
+        * e.g. -DUSE_STACK_SIZE=16384 */
+       (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
+#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
+
+       result = pthread_create(&thread_id, &attr, func, param);
+       pthread_attr_destroy(&attr);
+       if ((result == 0) && (threadidptr != NULL)) {
+               *threadidptr = thread_id;
+       }
+       return result;
+}
+
+
+/* Wait for a thread to finish. */
+static int
+mg_join_thread(pthread_t threadid)
+{
+       int result;
+
+       result = pthread_join(threadid, NULL);
+       return result;
+}
+
+
+#ifndef NO_CGI
+static pid_t
+spawn_process(struct mg_connection *conn,
+              const char *prog,
+              char *envblk,
+              char *envp[],
+              int fdin[2],
+              int fdout[2],
+              int fderr[2],
+              const char *dir)
+{
+       pid_t pid;
+       const char *interp;
+
+       (void)envblk;
+
+       if (conn == NULL) {
+               return 0;
+       }
+
+       if ((pid = fork()) == -1) {
+               /* Parent */
+               send_http_error(conn,
+                               500,
+                               "Error: Creating CGI process\nfork(): %s",
+                               strerror(ERRNO));
+       } else if (pid == 0) {
+               /* Child */
+               if (chdir(dir) != 0) {
+                       mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
+               } else if (dup2(fdin[0], 0) == -1) {
+                       mg_cry(conn,
+                              "%s: dup2(%d, 0): %s",
+                              __func__,
+                              fdin[0],
+                              strerror(ERRNO));
+               } else if (dup2(fdout[1], 1) == -1) {
+                       mg_cry(conn,
+                              "%s: dup2(%d, 1): %s",
+                              __func__,
+                              fdout[1],
+                              strerror(ERRNO));
+               } else if (dup2(fderr[1], 2) == -1) {
+                       mg_cry(conn,
+                              "%s: dup2(%d, 2): %s",
+                              __func__,
+                              fderr[1],
+                              strerror(ERRNO));
+               } else {
+                       /* Keep stderr and stdout in two different pipes.
+                        * Stdout will be sent back to the client,
+                        * stderr should go into a server error log. */
+                       (void)close(fdin[0]);
+                       (void)close(fdout[1]);
+                       (void)close(fderr[1]);
+
+                       /* Close write end fdin and read end fdout and fderr */
+                       (void)close(fdin[1]);
+                       (void)close(fdout[0]);
+                       (void)close(fderr[0]);
+
+                       /* After exec, all signal handlers are restored to their default
+                        * values, with one exception of SIGCHLD. According to
+                        * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will
+                        * leave unchanged after exec if it was set to be ignored. Restore
+                        * it to default action. */
+                       signal(SIGCHLD, SIG_DFL);
+
+                       interp = conn->ctx->config[CGI_INTERPRETER];
+                       if (interp == NULL) {
+                               (void)execle(prog, prog, NULL, envp);
+                               mg_cry(conn,
+                                      "%s: execle(%s): %s",
+                                      __func__,
+                                      prog,
+                                      strerror(ERRNO));
+                       } else {
+                               (void)execle(interp, interp, prog, NULL, envp);
+                               mg_cry(conn,
+                                      "%s: execle(%s %s): %s",
+                                      __func__,
+                                      interp,
+                                      prog,
+                                      strerror(ERRNO));
+                       }
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       return pid;
+}
+#endif /* !NO_CGI */
+
+
+static int
+set_non_blocking_mode(SOCKET sock)
+{
+       int flags;
+
+       flags = fcntl(sock, F_GETFL, 0);
+       (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+       return 0;
+}
+#endif /* _WIN32 */
+/* End of initial operating system specific define block. */
+
+
+/* Get a random number (independent of C rand function) */
+static uint64_t
+get_random(void)
+{
+       static uint64_t lfsr = 0; /* Linear feedback shift register */
+       static uint64_t lcg = 0;  /* Linear congruential generator */
+       struct timespec now;
+
+       memset(&now, 0, sizeof(now));
+       clock_gettime(CLOCK_MONOTONIC, &now);
+
+       if (lfsr == 0) {
+               /* lfsr will be only 0 if has not been initialized,
+                * so this code is called only once. */
+               lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec)
+                      ^ ((uint64_t)(ptrdiff_t)&now) ^ (((uint64_t)time(NULL)) << 33);
+               lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec
+                     + (uint64_t)(ptrdiff_t)&now;
+       } else {
+               /* Get the next step of both random number generators. */
+               lfsr = (lfsr >> 1)
+                      | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
+                         << 63);
+               lcg = lcg * 6364136223846793005 + 1442695040888963407;
+       }
+
+       /* Combining two pseudo-random number generators and a high resolution part
+        * of the current server time will make it hard (impossible?) to guess the
+        * next number. */
+       return (lfsr ^ lcg ^ (uint64_t)now.tv_nsec);
+}
+
+
+/* Write data to the IO channel - opened file descriptor, socket or SSL
+ * descriptor. Return number of bytes written. */
+static int
+push(struct mg_context *ctx,
+     FILE *fp,
+     SOCKET sock,
+     SSL *ssl,
+     const char *buf,
+     int len,
+     double timeout)
+{
+       struct timespec start, now;
+       int n, err;
+
+#ifdef _WIN32
+       typedef int len_t;
+#else
+       typedef size_t len_t;
+#endif
+
+       if (timeout > 0) {
+               memset(&start, 0, sizeof(start));
+               memset(&now, 0, sizeof(now));
+               clock_gettime(CLOCK_MONOTONIC, &start);
+       }
+
+       if (ctx == NULL) {
+               return -1;
+       }
+
+#ifdef NO_SSL
+       if (ssl) {
+               return -1;
+       }
+#endif
+
+       do {
+
+#ifndef NO_SSL
+               if (ssl != NULL) {
+                       n = SSL_write(ssl, buf, len);
+                       if (n <= 0) {
+                               err = SSL_get_error(ssl, n);
+                               if ((err == 5 /* SSL_ERROR_SYSCALL */) && (n == -1)) {
+                                       err = ERRNO;
+                               } else {
+                                       DEBUG_TRACE("SSL_write() failed, error %d", err);
+                                       return -1;
+                               }
+                       } else {
+                               err = 0;
+                       }
+               } else
+#endif
+                   if (fp != NULL) {
+                       n = (int)fwrite(buf, 1, (size_t)len, fp);
+                       if (ferror(fp)) {
+                               n = -1;
+                               err = ERRNO;
+                       } else {
+                               err = 0;
+                       }
+               } else {
+                       n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
+                       err = (n < 0) ? ERRNO : 0;
+               }
+
+               if (ctx->stop_flag) {
+                       return -1;
+               }
+
+               if ((n > 0) || (n == 0 && len == 0)) {
+                       /* some data has been read, or no data was requested */
+                       return n;
+               }
+               if (n == 0) {
+                       /* shutdown of the socket at client side */
+                       return -1;
+               }
+               if (n < 0) {
+                       /* socket error - check errno */
+                       DEBUG_TRACE("send() failed, error %d", err);
+
+                       /* TODO: error handling depending on the error code.
+                        * These codes are different between Windows and Linux.
+                        */
+                       return -1;
+               }
+
+               /* This code is not reached in the moment.
+                * ==> Fix the TODOs above first. */
+
+               if (timeout > 0) {
+                       clock_gettime(CLOCK_MONOTONIC, &now);
+               }
+
+       } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout));
+
+       (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
+                     used */
+
+       return -1;
+}
+
+
+static int64_t
+push_all(struct mg_context *ctx,
+         FILE *fp,
+         SOCKET sock,
+         SSL *ssl,
+         const char *buf,
+         int64_t len)
+{
+       double timeout = -1.0;
+       int64_t n, nwritten = 0;
+
+       if (ctx == NULL) {
+               return -1;
+       }
+
+       if (ctx->config[REQUEST_TIMEOUT]) {
+               timeout = atoi(ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+       }
+
+       while (len > 0 && ctx->stop_flag == 0) {
+               n = push(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout);
+               if (n < 0) {
+                       if (nwritten == 0) {
+                               nwritten = n; /* Propagate the error */
+                       }
+                       break;
+               } else if (n == 0) {
+                       break; /* No more data to write */
+               } else {
+                       nwritten += n;
+                       len -= n;
+               }
+       }
+
+       return nwritten;
+}
+
+
+/* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
+ * Return negative value on error, or number of bytes read on success. */
+static int
+pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
+{
+       int nread, err;
+       struct timespec start, now;
+
+#ifdef _WIN32
+       typedef int len_t;
+#else
+       typedef size_t len_t;
+#endif
+
+       if (timeout > 0) {
+               memset(&start, 0, sizeof(start));
+               memset(&now, 0, sizeof(now));
+               clock_gettime(CLOCK_MONOTONIC, &start);
+       }
+
+       do {
+               if (fp != NULL) {
+                       /* Use read() instead of fread(), because if we're reading from the
+                        * CGI pipe, fread() may block until IO buffer is filled up. We
+                        * cannot afford to block and must pass all read bytes immediately
+                        * to the client. */
+                       nread = (int)read(fileno(fp), buf, (size_t)len);
+                       err = (nread < 0) ? ERRNO : 0;
+
+#ifndef NO_SSL
+               } else if (conn->ssl != NULL) {
+                       nread = SSL_read(conn->ssl, buf, len);
+                       if (nread <= 0) {
+                               err = SSL_get_error(conn->ssl, nread);
+                               if ((err == 5 /* SSL_ERROR_SYSCALL */) && (nread == -1)) {
+                                       err = ERRNO;
+                               } else {
+                                       DEBUG_TRACE("SSL_read() failed, error %d", err);
+                                       return -1;
+                               }
+                       } else {
+                               err = 0;
+                       }
+#endif
+
+               } else {
+                       nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
+                       err = (nread < 0) ? ERRNO : 0;
+               }
+
+               if (conn->ctx->stop_flag) {
+                       return -1;
+               }
+
+               if ((nread > 0) || (nread == 0 && len == 0)) {
+                       /* some data has been read, or no data was requested */
+                       return nread;
+               }
+               if (nread == 0) {
+                       /* shutdown of the socket at client side */
+                       return -1;
+               }
+               if (nread < 0) {
+/* socket error - check errno */
+#ifdef _WIN32
+                       if (err == WSAEWOULDBLOCK) {
+                               /* standard case if called from close_socket_gracefully */
+                               return -1;
+                       } else if (err == WSAETIMEDOUT) {
+                               /* timeout is handled by the while loop  */
+                       } else {
+                               DEBUG_TRACE("recv() failed, error %d", err);
+                               return -1;
+                       }
+#else
+                       /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
+                        * if the timeout is reached and if the socket was set to non-
+                        * blocking in close_socket_gracefully, so we can not distinguish
+                        * here. We have to wait for the timeout in both cases for now.
+                        */
+                       if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {
+                               /* EAGAIN/EWOULDBLOCK:
+                                * standard case if called from close_socket_gracefully
+                                * => should return -1 */
+                               /* or timeout occured
+                                * => the code must stay in the while loop */
+
+                               /* EINTR can be generated on a socket with a timeout set even
+                                * when SA_RESTART is effective for all relevant signals
+                                * (see signal(7)).
+                                * => stay in the while loop */
+                       } else {
+                               DEBUG_TRACE("recv() failed, error %d", err);
+                               return -1;
+                       }
+#endif
+               }
+               if (timeout > 0) {
+                       clock_gettime(CLOCK_MONOTONIC, &now);
+               }
+       } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout));
+
+       /* Timeout occured, but no data available. */
+       return -1;
+}
+
+
+static int
+pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
+{
+       int n, nread = 0;
+       double timeout = -1.0;
+
+       if (conn->ctx->config[REQUEST_TIMEOUT]) {
+               timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+       }
+
+       while (len > 0 && conn->ctx->stop_flag == 0) {
+               n = pull(fp, conn, buf + nread, len, timeout);
+               if (n < 0) {
+                       if (nread == 0) {
+                               nread = n; /* Propagate the error */
+                       }
+                       break;
+               } else if (n == 0) {
+                       break; /* No more data to read */
+               } else {
+                       conn->consumed_content += n;
+                       nread += n;
+                       len -= n;
+               }
+       }
+
+       return nread;
+}
+
+
+static void
+discard_unread_request_data(struct mg_connection *conn)
+{
+       char buf[MG_BUF_LEN];
+       size_t to_read;
+       int nread;
+
+       if (conn == NULL) {
+               return;
+       }
+
+       to_read = sizeof(buf);
+
+       if (conn->is_chunked) {
+               /* Chunked encoding: 1=chunk not read completely, 2=chunk read
+                * completely */
+               while (conn->is_chunked == 1) {
+                       nread = mg_read(conn, buf, to_read);
+                       if (nread <= 0) {
+                               break;
+                       }
+               }
+
+       } else {
+               /* Not chunked: content length is known */
+               while (conn->consumed_content < conn->content_len) {
+                       if (to_read
+                           > (size_t)(conn->content_len - conn->consumed_content)) {
+                               to_read = (size_t)(conn->content_len - conn->consumed_content);
+                       }
+
+                       nread = mg_read(conn, buf, to_read);
+                       if (nread <= 0) {
+                               break;
+                       }
+               }
+       }
+}
+
+
+static int
+mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
+{
+       int64_t n, buffered_len, nread;
+       int64_t len64 =
+           (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is
+                                                      * int, we may not read more
+                                                      * bytes */
+       const char *body;
+
+       if (conn == NULL) {
+               return 0;
+       }
+
+       /* If Content-Length is not set for a PUT or POST request, read until
+        * socket is closed */
+       if (conn->consumed_content == 0 && conn->content_len == -1) {
+               conn->content_len = INT64_MAX;
+               conn->must_close = 1;
+       }
+
+       nread = 0;
+       if (conn->consumed_content < conn->content_len) {
+               /* Adjust number of bytes to read. */
+               int64_t left_to_read = conn->content_len - conn->consumed_content;
+               if (left_to_read < len64) {
+                       /* Do not read more than the total content length of the request.
+                        */
+                       len64 = left_to_read;
+               }
+
+               /* Return buffered data */
+               buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
+                              - conn->consumed_content;
+               if (buffered_len > 0) {
+                       if (len64 < buffered_len) {
+                               buffered_len = len64;
+                       }
+                       body = conn->buf + conn->request_len + conn->consumed_content;
+                       memcpy(buf, body, (size_t)buffered_len);
+                       len64 -= buffered_len;
+                       conn->consumed_content += buffered_len;
+                       nread += buffered_len;
+                       buf = (char *)buf + buffered_len;
+               }
+
+               /* We have returned all buffered data. Read new data from the remote
+                * socket.
+                */
+               if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
+                       nread += n;
+               } else {
+                       nread = (nread > 0 ? nread : n);
+               }
+       }
+       return (int)nread;
+}
+
+
+static char
+mg_getc(struct mg_connection *conn)
+{
+       char c;
+       if (conn == NULL) {
+               return 0;
+       }
+       conn->content_len++;
+       if (mg_read_inner(conn, &c, 1) <= 0) {
+               return (char)0;
+       }
+       return c;
+}
+
+
+int
+mg_read(struct mg_connection *conn, void *buf, size_t len)
+{
+       if (len > INT_MAX) {
+               len = INT_MAX;
+       }
+
+       if (conn == NULL) {
+               return 0;
+       }
+
+       if (conn->is_chunked) {
+               size_t all_read = 0;
+
+               while (len > 0) {
+
+                       if (conn->is_chunked == 2) {
+                               /* No more data left to read */
+                               return 0;
+                       }
+
+                       if (conn->chunk_remainder) {
+                               /* copy from the remainder of the last received chunk */
+                               long read_ret;
+                               size_t read_now =
+                                   ((conn->chunk_remainder > len) ? (len)
+                                                                  : (conn->chunk_remainder));
+
+                               conn->content_len += (int)read_now;
+                               read_ret =
+                                   mg_read_inner(conn, (char *)buf + all_read, read_now);
+                               all_read += (size_t)read_ret;
+
+                               conn->chunk_remainder -= read_now;
+                               len -= read_now;
+
+                               if (conn->chunk_remainder == 0) {
+                                       /* the rest of the data in the current chunk has been read
+                                        */
+                                       if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) {
+                                               /* Protocol violation */
+                                               return -1;
+                                       }
+                               }
+
+                       } else {
+                               /* fetch a new chunk */
+                               int i = 0;
+                               char lenbuf[64];
+                               char *end = 0;
+                               unsigned long chunkSize = 0;
+
+                               for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) {
+                                       lenbuf[i] = mg_getc(conn);
+                                       if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') {
+                                               continue;
+                                       }
+                                       if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') {
+                                               lenbuf[i + 1] = 0;
+                                               chunkSize = strtoul(lenbuf, &end, 16);
+                                               if (chunkSize == 0) {
+                                                       /* regular end of content */
+                                                       conn->is_chunked = 2;
+                                               }
+                                               break;
+                                       }
+                                       if (!isalnum(lenbuf[i])) {
+                                               /* illegal character for chunk length */
+                                               return -1;
+                                       }
+                               }
+                               if ((end == NULL) || (*end != '\r')) {
+                                       /* chunksize not set correctly */
+                                       return -1;
+                               }
+                               if (chunkSize == 0) {
+                                       break;
+                               }
+
+                               conn->chunk_remainder = chunkSize;
+                       }
+               }
+
+               return (int)all_read;
+       }
+       return mg_read_inner(conn, buf, len);
+}
+
+
+int
+mg_write(struct mg_connection *conn, const void *buf, size_t len)
+{
+       time_t now;
+       int64_t n, total, allowed;
+
+       if (conn == NULL) {
+               return 0;
+       }
+
+       if (conn->throttle > 0) {
+               if ((now = time(NULL)) != conn->last_throttle_time) {
+                       conn->last_throttle_time = now;
+                       conn->last_throttle_bytes = 0;
+               }
+               allowed = conn->throttle - conn->last_throttle_bytes;
+               if (allowed > (int64_t)len) {
+                       allowed = (int64_t)len;
+               }
+               if ((total = push_all(conn->ctx,
+                                     NULL,
+                                     conn->client.sock,
+                                     conn->ssl,
+                                     (const char *)buf,
+                                     (int64_t)allowed)) == allowed) {
+                       buf = (const char *)buf + total;
+                       conn->last_throttle_bytes += total;
+                       while (total < (int64_t)len && conn->ctx->stop_flag == 0) {
+                               allowed = conn->throttle > (int64_t)len - total
+                                             ? (int64_t)len - total
+                                             : conn->throttle;
+                               if ((n = push_all(conn->ctx,
+                                                 NULL,
+                                                 conn->client.sock,
+                                                 conn->ssl,
+                                                 (const char *)buf,
+                                                 (int64_t)allowed)) != allowed) {
+                                       break;
+                               }
+                               sleep(1);
+                               conn->last_throttle_bytes = allowed;
+                               conn->last_throttle_time = time(NULL);
+                               buf = (const char *)buf + n;
+                               total += n;
+                       }
+               }
+       } else {
+               total = push_all(conn->ctx,
+                                NULL,
+                                conn->client.sock,
+                                conn->ssl,
+                                (const char *)buf,
+                                (int64_t)len);
+       }
+       return (int)total;
+}
+
+
+/* Alternative alloc_vprintf() for non-compliant C runtimes */
+static int
+alloc_vprintf2(char **buf, const char *fmt, va_list ap)
+{
+       va_list ap_copy;
+       size_t size = MG_BUF_LEN / 4;
+       int len = -1;
+
+       *buf = NULL;
+       while (len < 0) {
+               if (*buf) {
+                       mg_free(*buf);
+               }
+
+               size *= 4;
+               *buf = (char *)mg_malloc(size);
+               if (!*buf) {
+                       break;
+               }
+
+               va_copy(ap_copy, ap);
+               len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
+               va_end(ap_copy);
+               (*buf)[size - 1] = 0;
+       }
+
+       return len;
+}
+
+
+/* Print message to buffer. If buffer is large enough to hold the message,
+ * return buffer. If buffer is to small, allocate large enough buffer on heap,
+ * and return allocated buffer. */
+static int
+alloc_vprintf(char **out_buf,
+              char *prealloc_buf,
+              size_t prealloc_size,
+              const char *fmt,
+              va_list ap)
+{
+       va_list ap_copy;
+       int len;
+
+       /* Windows is not standard-compliant, and vsnprintf() returns -1 if
+        * buffer is too small. Also, older versions of msvcrt.dll do not have
+        * _vscprintf().  However, if size is 0, vsnprintf() behaves correctly.
+        * Therefore, we make two passes: on first pass, get required message
+        * length.
+        * On second pass, actually print the message. */
+       va_copy(ap_copy, ap);
+       len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
+       va_end(ap_copy);
+
+       if (len < 0) {
+               /* C runtime is not standard compliant, vsnprintf() returned -1.
+                * Switch to alternative code path that uses incremental allocations.
+               */
+               va_copy(ap_copy, ap);
+               len = alloc_vprintf2(out_buf, fmt, ap);
+               va_end(ap_copy);
+
+       } else if ((size_t)(len) >= prealloc_size) {
+               /* The pre-allocated buffer not large enough. */
+               /* Allocate a new buffer. */
+               *out_buf = (char *)mg_malloc((size_t)(len) + 1);
+               if (!*out_buf) {
+                       /* Allocation failed. Return -1 as "out of memory" error. */
+                       return -1;
+               }
+               /* Buffer allocation successful. Store the string there. */
+               va_copy(ap_copy, ap);
+               IGNORE_UNUSED_RESULT(
+                   vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
+               va_end(ap_copy);
+
+       } else {
+               /* The pre-allocated buffer is large enough.
+                * Use it to store the string and return the address. */
+               va_copy(ap_copy, ap);
+               IGNORE_UNUSED_RESULT(
+                   vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
+               va_end(ap_copy);
+               *out_buf = prealloc_buf;
+       }
+
+       return len;
+}
+
+
+static int
+mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
+{
+       char mem[MG_BUF_LEN];
+       char *buf = NULL;
+       int len;
+
+       if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
+               len = mg_write(conn, buf, (size_t)len);
+       }
+       if (buf != mem && buf != NULL) {
+               mg_free(buf);
+       }
+
+       return len;
+}
+
+
+int
+mg_printf(struct mg_connection *conn, const char *fmt, ...)
+{
+       va_list ap;
+       int result;
+
+       va_start(ap, fmt);
+       result = mg_vprintf(conn, fmt, ap);
+       va_end(ap);
+
+       return result;
+}
+
+
+int
+mg_url_decode(const char *src,
+              int src_len,
+              char *dst,
+              int dst_len,
+              int is_form_url_encoded)
+{
+       int i, j, a, b;
+#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
+
+       for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
+               if (i < src_len - 2 && src[i] == '%'
+                   && isxdigit(*(const unsigned char *)(src + i + 1))
+                   && isxdigit(*(const unsigned char *)(src + i + 2))) {
+                       a = tolower(*(const unsigned char *)(src + i + 1));
+                       b = tolower(*(const unsigned char *)(src + i + 2));
+                       dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
+                       i += 2;
+               } else if (is_form_url_encoded && src[i] == '+') {
+                       dst[j] = ' ';
+               } else {
+                       dst[j] = src[i];
+               }
+       }
+
+       dst[j] = '\0'; /* Null-terminate the destination */
+
+       return i >= src_len ? j : -1;
+}
+
+
+int
+mg_get_var(const char *data,
+           size_t data_len,
+           const char *name,
+           char *dst,
+           size_t dst_len)
+{
+       return mg_get_var2(data, data_len, name, dst, dst_len, 0);
+}
+
+
+int
+mg_get_var2(const char *data,
+            size_t data_len,
+            const char *name,
+            char *dst,
+            size_t dst_len,
+            size_t occurrence)
+{
+       const char *p, *e, *s;
+       size_t name_len;
+       int len;
+
+       if (dst == NULL || dst_len == 0) {
+               len = -2;
+       } else if (data == NULL || name == NULL || data_len == 0) {
+               len = -1;
+               dst[0] = '\0';
+       } else {
+               name_len = strlen(name);
+               e = data + data_len;
+               len = -1;
+               dst[0] = '\0';
+
+               /* data is "var1=val1&var2=val2...". Find variable first */
+               for (p = data; p + name_len < e; p++) {
+                       if ((p == data || p[-1] == '&') && p[name_len] == '='
+                           && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
+                               /* Point p to variable value */
+                               p += name_len + 1;
+
+                               /* Point s to the end of the value */
+                               s = (const char *)memchr(p, '&', (size_t)(e - p));
+                               if (s == NULL) {
+                                       s = e;
+                               }
+                               /* assert(s >= p); */
+                               if (s < p) {
+                                       return -3;
+                               }
+
+                               /* Decode variable into destination buffer */
+                               len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
+
+                               /* Redirect error code from -1 to -2 (destination buffer too
+                                * small). */
+                               if (len == -1) {
+                                       len = -2;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       return len;
+}
+
+
+int
+mg_get_cookie(const char *cookie_header,
+              const char *var_name,
+              char *dst,
+              size_t dst_size)
+{
+       const char *s, *p, *end;
+       int name_len, len = -1;
+
+       if (dst == NULL || dst_size == 0) {
+               len = -2;
+       } else if (var_name == NULL || (s = cookie_header) == NULL) {
+               len = -1;
+               dst[0] = '\0';
+       } else {
+               name_len = (int)strlen(var_name);
+               end = s + strlen(s);
+               dst[0] = '\0';
+
+               for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
+                       if (s[name_len] == '=') {
+                               s += name_len + 1;
+                               if ((p = strchr(s, ' ')) == NULL) {
+                                       p = end;
+                               }
+                               if (p[-1] == ';') {
+                                       p--;
+                               }
+                               if (*s == '"' && p[-1] == '"' && p > s + 1) {
+                                       s++;
+                                       p--;
+                               }
+                               if ((size_t)(p - s) < dst_size) {
+                                       len = (int)(p - s);
+                                       mg_strlcpy(dst, s, (size_t)len + 1);
+                               } else {
+                                       len = -3;
+                               }
+                               break;
+                       }
+               }
+       }
+       return len;
+}
+
+
+#if defined(USE_WEBSOCKET) || defined(USE_LUA)
+static void
+base64_encode(const unsigned char *src, int src_len, char *dst)
+{
+       static const char *b64 =
+           "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+       int i, j, a, b, c;
+
+       for (i = j = 0; i < src_len; i += 3) {
+               a = src[i];
+               b = i + 1 >= src_len ? 0 : src[i + 1];
+               c = i + 2 >= src_len ? 0 : src[i + 2];
+
+               dst[j++] = b64[a >> 2];
+               dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
+               if (i + 1 < src_len) {
+                       dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
+               }
+               if (i + 2 < src_len) {
+                       dst[j++] = b64[c & 63];
+               }
+       }
+       while (j % 4 != 0) {
+               dst[j++] = '=';
+       }
+       dst[j++] = '\0';
+}
+#endif
+
+
+#if defined(USE_LUA)
+static unsigned char
+b64reverse(char letter)
+{
+       if (letter >= 'A' && letter <= 'Z') {
+               return letter - 'A';
+       }
+       if (letter >= 'a' && letter <= 'z') {
+               return letter - 'a' + 26;
+       }
+       if (letter >= '0' && letter <= '9') {
+               return letter - '0' + 52;
+       }
+       if (letter == '+') {
+               return 62;
+       }
+       if (letter == '/') {
+               return 63;
+       }
+       if (letter == '=') {
+               return 255; /* normal end */
+       }
+       return 254; /* error */
+}
+
+
+static int
+base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len)
+{
+       int i;
+       unsigned char a, b, c, d;
+
+       *dst_len = 0;
+
+       for (i = 0; i < src_len; i += 4) {
+               a = b64reverse(src[i]);
+               if (a >= 254) {
+                       return i;
+               }
+
+               b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]);
+               if (b >= 254) {
+                       return i + 1;
+               }
+
+               c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]);
+               if (c == 254) {
+                       return i + 2;
+               }
+
+               d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]);
+               if (d == 254) {
+                       return i + 3;
+               }
+
+               dst[(*dst_len)++] = (a << 2) + (b >> 4);
+               if (c != 255) {
+                       dst[(*dst_len)++] = (b << 4) + (c >> 2);
+                       if (d != 255) {
+                               dst[(*dst_len)++] = (c << 6) + d;
+                       }
+               }
+       }
+       return -1;
+}
+#endif
+
+
+static int
+is_put_or_delete_method(const struct mg_connection *conn)
+{
+       if (conn) {
+               const char *s = conn->request_info.request_method;
+               return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
+                                    || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH"));
+       }
+       return 0;
+}
+
+
+static void
+interpret_uri(struct mg_connection *conn,   /* in: request (must be valid) */
+              char *filename,               /* out: filename */
+              size_t filename_buf_len,      /* in: size of filename buffer */
+              struct file *filep,           /* out: file structure */
+              int *is_found,                /* out: file is found (directly) */
+              int *is_script_resource,      /* out: handled by a script? */
+              int *is_websocket_request,    /* out: websocket connetion? */
+              int *is_put_or_delete_request /* out: put/delete a file? */
+              )
+{
+/* TODO (high): Restructure this function */
+
+#if !defined(NO_FILES)
+       const char *uri = conn->request_info.local_uri;
+       const char *root = conn->ctx->config[DOCUMENT_ROOT];
+       const char *rewrite;
+       struct vec a, b;
+       int match_len;
+       char gz_path[PATH_MAX];
+       char const *accept_encoding;
+       int truncated;
+#if !defined(NO_CGI) || defined(USE_LUA)
+       char *p;
+#endif
+#else
+       (void)filename_buf_len; /* unused if NO_FILES is defined */
+#endif
+
+       memset(filep, 0, sizeof(*filep));
+       *filename = 0;
+       *is_found = 0;
+       *is_script_resource = 0;
+       *is_put_or_delete_request = is_put_or_delete_method(conn);
+
+#if defined(USE_WEBSOCKET)
+       *is_websocket_request = is_websocket_protocol(conn);
+#if !defined(NO_FILES)
+       if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) {
+               root = conn->ctx->config[WEBSOCKET_ROOT];
+       }
+#endif /* !NO_FILES */
+#else  /* USE_WEBSOCKET */
+       *is_websocket_request = 0;
+#endif /* USE_WEBSOCKET */
+
+#if !defined(NO_FILES)
+       /* Note that root == NULL is a regular use case here. This occurs,
+        * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
+        * config is not required. */
+       if (root == NULL) {
+               /* all file related outputs have already been set to 0, just return
+                */
+               return;
+       }
+
+       /* Using buf_len - 1 because memmove() for PATH_INFO may shift part
+        * of the path one byte on the right.
+        * If document_root is NULL, leave the file empty. */
+       mg_snprintf(
+           conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
+
+       if (truncated) {
+               goto interpret_cleanup;
+       }
+
+       rewrite = conn->ctx->config[REWRITE];
+       while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
+               if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
+                       mg_snprintf(conn,
+                                   &truncated,
+                                   filename,
+                                   filename_buf_len - 1,
+                                   "%.*s%s",
+                                   (int)b.len,
+                                   b.ptr,
+                                   uri + match_len);
+                       break;
+               }
+       }
+
+       if (truncated) {
+               goto interpret_cleanup;
+       }
+
+       /* Local file path and name, corresponding to requested URI
+        * is now stored in "filename" variable. */
+       if (mg_stat(conn, filename, filep)) {
+#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
+               /* File exists. Check if it is a script type. */
+               if (0
+#if !defined(NO_CGI)
+                   || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+                                   strlen(conn->ctx->config[CGI_EXTENSIONS]),
+                                   filename) > 0
+#endif
+#if defined(USE_LUA)
+                   || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+                                   strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+                                   filename) > 0
+#endif
+#if defined(USE_DUKTAPE)
+                   || match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+                                   strlen(
+                                       conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+                                   filename) > 0
+#endif
+                   ) {
+                       /* The request addresses a CGI script or a Lua script. The URI
+                        * corresponds to the script itself (like /path/script.cgi),
+                        * and there is no additional resource path
+                        * (like /path/script.cgi/something).
+                        * Requests that modify (replace or delete) a resource, like
+                        * PUT and DELETE requests, should replace/delete the script
+                        * file.
+                        * Requests that read or write from/to a resource, like GET and
+                        * POST requests, should call the script and return the
+                        * generated response. */
+                       *is_script_resource = !*is_put_or_delete_request;
+               }
+#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
+               *is_found = 1;
+               return;
+       }
+
+       /* If we can't find the actual file, look for the file
+        * with the same name but a .gz extension. If we find it,
+        * use that and set the gzipped flag in the file struct
+        * to indicate that the response need to have the content-
+        * encoding: gzip header.
+        * We can only do this if the browser declares support. */
+       if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
+               if (strstr(accept_encoding, "gzip") != NULL) {
+                       mg_snprintf(
+                           conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
+
+                       if (truncated) {
+                               goto interpret_cleanup;
+                       }
+
+                       if (mg_stat(conn, gz_path, filep)) {
+                               if (filep) {
+                                       filep->gzipped = 1;
+                                       *is_found = 1;
+                               }
+                               /* Currently gz files can not be scripts. */
+                               return;
+                       }
+               }
+       }
+
+#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
+       /* Support PATH_INFO for CGI scripts. */
+       for (p = filename + strlen(filename); p > filename + 1; p--) {
+               if (*p == '/') {
+                       *p = '\0';
+                       if ((0
+#if !defined(NO_CGI)
+                            || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+                                            strlen(conn->ctx->config[CGI_EXTENSIONS]),
+                                            filename) > 0
+#endif
+#if defined(USE_LUA)
+                            || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+                                            strlen(
+                                                conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+                                            filename) > 0
+#endif
+#if defined(USE_DUKTAPE)
+                            || match_prefix(
+                                   conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+                                   strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+                                   filename) > 0
+#endif
+                            ) && mg_stat(conn, filename, filep)) {
+                               /* Shift PATH_INFO block one character right, e.g.
+                                * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
+                                * conn->path_info is pointing to the local variable "path"
+                                * declared in handle_request(), so PATH_INFO is not valid
+                                * after handle_request returns. */
+                               conn->path_info = p + 1;
+                               memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
+                                                                          * trailing \0 */
+                               p[1] = '/';
+                               *is_script_resource = 1;
+                               break;
+                       } else {
+                               *p = '/';
+                       }
+               }
+       }
+#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
+#endif /* !defined(NO_FILES) */
+       return;
+
+#if !defined(NO_FILES)
+/* Reset all outputs */
+interpret_cleanup:
+       memset(filep, 0, sizeof(*filep));
+       *filename = 0;
+       *is_found = 0;
+       *is_script_resource = 0;
+       *is_websocket_request = 0;
+       *is_put_or_delete_request = 0;
+#endif /* !defined(NO_FILES) */
+}
+
+
+/* Check whether full request is buffered. Return:
+ * -1  if request is malformed
+ *  0  if request is not yet fully buffered
+ * >0  actual request length, including last \r\n\r\n */
+static int
+get_request_len(const char *buf, int buflen)
+{
+       const char *s, *e;
+       int len = 0;
+
+       for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
+               /* Control characters are not allowed but >=128 is. */
+               if (!isprint(*(const unsigned char *)s) && *s != '\r' && *s != '\n'
+                   && *(const unsigned char *)s < 128) {
+                       len = -1;
+                       break; /* [i_a] abort scan as soon as one malformed character is
+                               * found; */
+                       /* don't let subsequent \r\n\r\n win us over anyhow */
+               } else if (s[0] == '\n' && s[1] == '\n') {
+                       len = (int)(s - buf) + 2;
+               } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') {
+                       len = (int)(s - buf) + 3;
+               }
+
+       return len;
+}
+
+
+#if !defined(NO_CACHING)
+/* Convert month to the month number. Return -1 on error, or month number */
+static int
+get_month_index(const char *s)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(month_names); i++) {
+               if (!strcmp(s, month_names[i])) {
+                       return (int)i;
+               }
+       }
+
+       return -1;
+}
+
+
+/* Parse UTC date-time string, and return the corresponding time_t value. */
+static time_t
+parse_date_string(const char *datetime)
+{
+       char month_str[32] = {0};
+       int second, minute, hour, day, month, year;
+       time_t result = (time_t)0;
+       struct tm tm;
+
+       if ((sscanf(datetime,
+                   "%d/%3s/%d %d:%d:%d",
+                   &day,
+                   month_str,
+                   &year,
+                   &hour,
+                   &minute,
+                   &second) == 6) || (sscanf(datetime,
+                                             "%d %3s %d %d:%d:%d",
+                                             &day,
+                                             month_str,
+                                             &year,
+                                             &hour,
+                                             &minute,
+                                             &second) == 6)
+           || (sscanf(datetime,
+                      "%*3s, %d %3s %d %d:%d:%d",
+                      &day,
+                      month_str,
+                      &year,
+                      &hour,
+                      &minute,
+                      &second) == 6) || (sscanf(datetime,
+                                                "%d-%3s-%d %d:%d:%d",
+                                                &day,
+                                                month_str,
+                                                &year,
+                                                &hour,
+                                                &minute,
+                                                &second) == 6)) {
+               month = get_month_index(month_str);
+               if ((month >= 0) && (year >= 1970)) {
+                       memset(&tm, 0, sizeof(tm));
+                       tm.tm_year = year - 1900;
+                       tm.tm_mon = month;
+                       tm.tm_mday = day;
+                       tm.tm_hour = hour;
+                       tm.tm_min = minute;
+                       tm.tm_sec = second;
+                       result = timegm(&tm);
+               }
+       }
+
+       return result;
+}
+#endif /* !NO_CACHING */
+
+
+/* Protect against directory disclosure attack by removing '..',
+ * excessive '/' and '\' characters */
+static void
+remove_double_dots_and_double_slashes(char *s)
+{
+       char *p = s;
+
+       while (*s != '\0') {
+               *p++ = *s++;
+               if (s[-1] == '/' || s[-1] == '\\') {
+                       /* Skip all following slashes, backslashes and double-dots */
+                       while (s[0] != '\0') {
+                               if (s[0] == '/' || s[0] == '\\') {
+                                       s++;
+                               } else if (s[0] == '.' && s[1] == '.') {
+                                       s += 2;
+                               } else {
+                                       break;
+                               }
+                       }
+               }
+       }
+       *p = '\0';
+}
+
+
+static const struct {
+       const char *extension;
+       size_t ext_len;
+       const char *mime_type;
+} builtin_mime_types[] = {
+    /* IANA registered MIME types (http://www.iana.org/assignments/media-types)
+     * application types */
+    {".doc", 4, "application/msword"},
+    {".eps", 4, "application/postscript"},
+    {".exe", 4, "application/octet-stream"},
+    {".js", 3, "application/javascript"},
+    {".json", 5, "application/json"},
+    {".pdf", 4, "application/pdf"},
+    {".ps", 3, "application/postscript"},
+    {".rtf", 4, "application/rtf"},
+    {".xhtml", 6, "application/xhtml+xml"},
+    {".xsl", 4, "application/xml"},
+    {".xslt", 5, "application/xml"},
+
+    /* fonts */
+    {".ttf", 4, "application/font-sfnt"},
+    {".cff", 4, "application/font-sfnt"},
+    {".otf", 4, "application/font-sfnt"},
+    {".aat", 4, "application/font-sfnt"},
+    {".sil", 4, "application/font-sfnt"},
+    {".pfr", 4, "application/font-tdpfr"},
+    {".woff", 5, "application/font-woff"},
+
+    /* audio */
+    {".mp3", 4, "audio/mpeg"},
+    {".oga", 4, "audio/ogg"},
+    {".ogg", 4, "audio/ogg"},
+
+    /* image */
+    {".gif", 4, "image/gif"},
+    {".ief", 4, "image/ief"},
+    {".jpeg", 5, "image/jpeg"},
+    {".jpg", 4, "image/jpeg"},
+    {".jpm", 4, "image/jpm"},
+    {".jpx", 4, "image/jpx"},
+    {".png", 4, "image/png"},
+    {".svg", 4, "image/svg+xml"},
+    {".tif", 4, "image/tiff"},
+    {".tiff", 5, "image/tiff"},
+
+    /* model */
+    {".wrl", 4, "model/vrml"},
+
+    /* text */
+    {".css", 4, "text/css"},
+    {".csv", 4, "text/csv"},
+    {".htm", 4, "text/html"},
+    {".html", 5, "text/html"},
+    {".sgm", 4, "text/sgml"},
+    {".shtm", 5, "text/html"},
+    {".shtml", 6, "text/html"},
+    {".txt", 4, "text/plain"},
+    {".xml", 4, "text/xml"},
+
+    /* video */
+    {".mov", 4, "video/quicktime"},
+    {".mp4", 4, "video/mp4"},
+    {".mpeg", 5, "video/mpeg"},
+    {".mpg", 4, "video/mpeg"},
+    {".ogv", 4, "video/ogg"},
+    {".qt", 3, "video/quicktime"},
+
+    /* not registered types
+     * (http://reference.sitepoint.com/html/mime-types-full,
+     * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */
+    {".arj", 4, "application/x-arj-compressed"},
+    {".gz", 3, "application/x-gunzip"},
+    {".rar", 4, "application/x-arj-compressed"},
+    {".swf", 4, "application/x-shockwave-flash"},
+    {".tar", 4, "application/x-tar"},
+    {".tgz", 4, "application/x-tar-gz"},
+    {".torrent", 8, "application/x-bittorrent"},
+    {".ppt", 4, "application/x-mspowerpoint"},
+    {".xls", 4, "application/x-msexcel"},
+    {".zip", 4, "application/x-zip-compressed"},
+    {".aac",
+     4,
+     "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */
+    {".aif", 4, "audio/x-aif"},
+    {".m3u", 4, "audio/x-mpegurl"},
+    {".mid", 4, "audio/x-midi"},
+    {".ra", 3, "audio/x-pn-realaudio"},
+    {".ram", 4, "audio/x-pn-realaudio"},
+    {".wav", 4, "audio/x-wav"},
+    {".bmp", 4, "image/bmp"},
+    {".ico", 4, "image/x-icon"},
+    {".pct", 4, "image/x-pct"},
+    {".pict", 5, "image/pict"},
+    {".rgb", 4, "image/x-rgb"},
+    {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */
+    {".asf", 4, "video/x-ms-asf"},
+    {".avi", 4, "video/x-msvideo"},
+    {".m4v", 4, "video/x-m4v"},
+    {NULL, 0, NULL}};
+
+
+const char *
+mg_get_builtin_mime_type(const char *path)
+{
+       const char *ext;
+       size_t i, path_len;
+
+       path_len = strlen(path);
+
+       for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
+               ext = path + (path_len - builtin_mime_types[i].ext_len);
+               if (path_len > builtin_mime_types[i].ext_len
+                   && mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
+                       return builtin_mime_types[i].mime_type;
+               }
+       }
+
+       return "text/plain";
+}
+
+
+/* Look at the "path" extension and figure what mime type it has.
+ * Store mime type in the vector. */
+static void
+get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
+{
+       struct vec ext_vec, mime_vec;
+       const char *list, *ext;
+       size_t path_len;
+
+       path_len = strlen(path);
+
+       if (ctx == NULL || vec == NULL) {
+               return;
+       }
+
+       /* Scan user-defined mime types first, in case user wants to
+        * override default mime types. */
+       list = ctx->config[EXTRA_MIME_TYPES];
+       while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
+               /* ext now points to the path suffix */
+               ext = path + path_len - ext_vec.len;
+               if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
+                       *vec = mime_vec;
+                       return;
+               }
+       }
+
+       vec->ptr = mg_get_builtin_mime_type(path);
+       vec->len = strlen(vec->ptr);
+}
+
+
+/* Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+       static const char *hex = "0123456789abcdef";
+
+       for (; len--; p++) {
+               *to++ = hex[p[0] >> 4];
+               *to++ = hex[p[0] & 0x0f];
+       }
+       *to = '\0';
+}
+
+
+/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */
+char *
+mg_md5(char buf[33], ...)
+{
+       md5_byte_t hash[16];
+       const char *p;
+       va_list ap;
+       md5_state_t ctx;
+
+       md5_init(&ctx);
+
+       va_start(ap, buf);
+       while ((p = va_arg(ap, const char *)) != NULL) {
+               md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
+       }
+       va_end(ap);
+
+       md5_finish(&ctx, hash);
+       bin2str(buf, hash, sizeof(hash));
+       return buf;
+}
+
+
+/* Check the user's password, return 1 if OK */
+static int
+check_password(const char *method,
+               const char *ha1,
+               const char *uri,
+               const char *nonce,
+               const char *nc,
+               const char *cnonce,
+               const char *qop,
+               const char *response)
+{
+       char ha2[32 + 1], expected_response[32 + 1];
+
+       /* Some of the parameters may be NULL */
+       if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL
+           || qop == NULL
+           || response == NULL) {
+               return 0;
+       }
+
+       /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
+       if (strlen(response) != 32) {
+               return 0;
+       }
+
+       mg_md5(ha2, method, ":", uri, NULL);
+       mg_md5(expected_response,
+              ha1,
+              ":",
+              nonce,
+              ":",
+              nc,
+              ":",
+              cnonce,
+              ":",
+              qop,
+              ":",
+              ha2,
+              NULL);
+
+       return mg_strcasecmp(response, expected_response) == 0;
+}
+
+
+/* Use the global passwords file, if specified by auth_gpass option,
+ * or search for .htpasswd in the requested directory. */
+static void
+open_auth_file(struct mg_connection *conn, const char *path, struct file *filep)
+{
+       if (conn != NULL && conn->ctx != NULL) {
+               char name[PATH_MAX];
+               const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
+               struct file file = STRUCT_FILE_INITIALIZER;
+               int truncated;
+
+               if (gpass != NULL) {
+                       /* Use global passwords file */
+                       if (!mg_fopen(conn, gpass, "r", filep)) {
+#ifdef DEBUG
+                               mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
+#endif
+                       }
+                       /* Important: using local struct file to test path for is_directory
+                        * flag. If filep is used, mg_stat() makes it appear as if auth file
+                        * was opened. */
+               } else if (mg_stat(conn, path, &file) && file.is_directory) {
+                       mg_snprintf(conn,
+                                   &truncated,
+                                   name,
+                                   sizeof(name),
+                                   "%s/%s",
+                                   path,
+                                   PASSWORDS_FILE_NAME);
+
+                       if (truncated || !mg_fopen(conn, name, "r", filep)) {
+#ifdef DEBUG
+                               mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO));
+#endif
+                       }
+               } else {
+                       /* Try to find .htpasswd in requested directory. */
+                       for (p = path, e = p + strlen(p) - 1; e > p; e--) {
+                               if (e[0] == '/') {
+                                       break;
+                               }
+                       }
+                       mg_snprintf(conn,
+                                   &truncated,
+                                   name,
+                                   sizeof(name),
+                                   "%.*s/%s",
+                                   (int)(e - p),
+                                   p,
+                                   PASSWORDS_FILE_NAME);
+
+                       if (truncated || !mg_fopen(conn, name, "r", filep)) {
+#ifdef DEBUG
+                               mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO));
+#endif
+                       }
+               }
+       }
+}
+
+
+/* Parsed Authorization header */
+struct ah {
+       char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
+};
+
+
+/* Return 1 on success. Always initializes the ah structure. */
+static int
+parse_auth_header(struct mg_connection *conn,
+                  char *buf,
+                  size_t buf_size,
+                  struct ah *ah)
+{
+       char *name, *value, *s;
+       const char *auth_header;
+       uint64_t nonce;
+
+       if (!ah || !conn) {
+               return 0;
+       }
+
+       (void)memset(ah, 0, sizeof(*ah));
+       if ((auth_header = mg_get_header(conn, "Authorization")) == NULL
+           || mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
+               return 0;
+       }
+
+       /* Make modifiable copy of the auth header */
+       (void)mg_strlcpy(buf, auth_header + 7, buf_size);
+       s = buf;
+
+       /* Parse authorization header */
+       for (;;) {
+               /* Gobble initial spaces */
+               while (isspace(*(unsigned char *)s)) {
+                       s++;
+               }
+               name = skip_quoted(&s, "=", " ", 0);
+               /* Value is either quote-delimited, or ends at first comma or space. */
+               if (s[0] == '\"') {
+                       s++;
+                       value = skip_quoted(&s, "\"", " ", '\\');
+                       if (s[0] == ',') {
+                               s++;
+                       }
+               } else {
+                       value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses
+                                                               * spaces */
+               }
+               if (*name == '\0') {
+                       break;
+               }
+
+               if (!strcmp(name, "username")) {
+                       ah->user = value;
+               } else if (!strcmp(name, "cnonce")) {
+                       ah->cnonce = value;
+               } else if (!strcmp(name, "response")) {
+                       ah->response = value;
+               } else if (!strcmp(name, "uri")) {
+                       ah->uri = value;
+               } else if (!strcmp(name, "qop")) {
+                       ah->qop = value;
+               } else if (!strcmp(name, "nc")) {
+                       ah->nc = value;
+               } else if (!strcmp(name, "nonce")) {
+                       ah->nonce = value;
+               }
+       }
+
+#ifndef NO_NONCE_CHECK
+       /* Read the nonce from the response. */
+       if (ah->nonce == NULL) {
+               return 0;
+       }
+       s = NULL;
+       nonce = strtoull(ah->nonce, &s, 10);
+       if ((s == NULL) || (*s != 0)) {
+               return 0;
+       }
+
+       /* Convert the nonce from the client to a number. */
+       nonce ^= conn->ctx->auth_nonce_mask;
+
+       /* The converted number corresponds to the time the nounce has been
+        * created. This should not be earlier than the server start. */
+       /* Server side nonce check is valuable in all situations but one:
+        * if the server restarts frequently, but the client should not see
+        * that, so the server should accept nonces from previous starts. */
+       /* However, the reasonable default is to not accept a nonce from a
+        * previous start, so if anyone changed the access rights between
+        * two restarts, a new login is required. */
+       if (nonce < (uint64_t)conn->ctx->start_time) {
+               /* nonce is from a previous start of the server and no longer valid
+                * (replay attack?) */
+               return 0;
+       }
+       /* Check if the nonce is too high, so it has not (yet) been used by the
+        * server. */
+       if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) {
+               return 0;
+       }
+#endif
+
+       /* CGI needs it as REMOTE_USER */
+       if (ah->user != NULL) {
+               conn->request_info.remote_user = mg_strdup(ah->user);
+       } else {
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static const char *
+mg_fgets(char *buf, size_t size, struct file *filep, char **p)
+{
+       const char *eof;
+       size_t len;
+       const char *memend;
+
+       if (!filep) {
+               return NULL;
+       }
+
+       if (filep->membuf != NULL && *p != NULL) {
+               memend = (const char *)&filep->membuf[filep->size];
+               /* Search for \n from p till the end of stream */
+               eof = (char *)memchr(*p, '\n', (size_t)(memend - *p));
+               if (eof != NULL) {
+                       eof += 1; /* Include \n */
+               } else {
+                       eof = memend; /* Copy remaining data */
+               }
+               len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p);
+               memcpy(buf, *p, len);
+               buf[len] = '\0';
+               *p += len;
+               return len ? eof : NULL;
+       } else if (filep->fp != NULL) {
+               return fgets(buf, (int)size, filep->fp);
+       } else {
+               return NULL;
+       }
+}
+
+struct read_auth_file_struct {
+       struct mg_connection *conn;
+       struct ah ah;
+       char *domain;
+       char buf[256 + 256 + 40];
+       char *f_user;
+       char *f_domain;
+       char *f_ha1;
+};
+
+
+static int
+read_auth_file(struct file *filep, struct read_auth_file_struct *workdata)
+{
+       char *p;
+       int is_authorized = 0;
+       struct file fp;
+       size_t l;
+
+       if (!filep || !workdata) {
+               return 0;
+       }
+
+       /* Loop over passwords file */
+       p = (char *)filep->membuf;
+       while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) {
+               l = strlen(workdata->buf);
+               while (l > 0) {
+                       if (isspace(workdata->buf[l - 1])
+                           || iscntrl(workdata->buf[l - 1])) {
+                               l--;
+                               workdata->buf[l] = 0;
+                       } else
+                               break;
+               }
+               if (l < 1) {
+                       continue;
+               }
+
+               workdata->f_user = workdata->buf;
+
+               if (workdata->f_user[0] == ':') {
+                       /* user names may not contain a ':' and may not be empty,
+                        * so lines starting with ':' may be used for a special purpose */
+                       if (workdata->f_user[1] == '#') {
+                               /* :# is a comment */
+                               continue;
+                       } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
+                               if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) {
+                                       is_authorized = read_auth_file(&fp, workdata);
+                                       mg_fclose(&fp);
+                               } else {
+                                       mg_cry(workdata->conn,
+                                              "%s: cannot open authorization file: %s",
+                                              __func__,
+                                              workdata->buf);
+                               }
+                               continue;
+                       }
+                       /* everything is invalid for the moment (might change in the
+                        * future) */
+                       mg_cry(workdata->conn,
+                              "%s: syntax error in authorization file: %s",
+                              __func__,
+                              workdata->buf);
+                       continue;
+               }
+
+               workdata->f_domain = strchr(workdata->f_user, ':');
+               if (workdata->f_domain == NULL) {
+                       mg_cry(workdata->conn,
+                              "%s: syntax error in authorization file: %s",
+                              __func__,
+                              workdata->buf);
+                       continue;
+               }
+               *(workdata->f_domain) = 0;
+               (workdata->f_domain)++;
+
+               workdata->f_ha1 = strchr(workdata->f_domain, ':');
+               if (workdata->f_ha1 == NULL) {
+                       mg_cry(workdata->conn,
+                              "%s: syntax error in authorization file: %s",
+                              __func__,
+                              workdata->buf);
+                       continue;
+               }
+               *(workdata->f_ha1) = 0;
+               (workdata->f_ha1)++;
+
+               if (!strcmp(workdata->ah.user, workdata->f_user)
+                   && !strcmp(workdata->domain, workdata->f_domain)) {
+                       return check_password(workdata->conn->request_info.request_method,
+                                             workdata->f_ha1,
+                                             workdata->ah.uri,
+                                             workdata->ah.nonce,
+                                             workdata->ah.nc,
+                                             workdata->ah.cnonce,
+                                             workdata->ah.qop,
+                                             workdata->ah.response);
+               }
+       }
+
+       return is_authorized;
+}
+
+
+/* Authorize against the opened passwords file. Return 1 if authorized. */
+static int
+authorize(struct mg_connection *conn, struct file *filep)
+{
+       struct read_auth_file_struct workdata;
+       char buf[MG_BUF_LEN];
+
+       if (!conn || !conn->ctx) {
+               return 0;
+       }
+
+       memset(&workdata, 0, sizeof(workdata));
+       workdata.conn = conn;
+
+       if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
+               return 0;
+       }
+       workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN];
+
+       return read_auth_file(filep, &workdata);
+}
+
+
+/* Return 1 if request is authorised, 0 otherwise. */
+static int
+check_authorization(struct mg_connection *conn, const char *path)
+{
+       char fname[PATH_MAX];
+       struct vec uri_vec, filename_vec;
+       const char *list;
+       struct file file = STRUCT_FILE_INITIALIZER;
+       int authorized = 1, truncated;
+
+       if (!conn || !conn->ctx) {
+               return 0;
+       }
+
+       list = conn->ctx->config[PROTECT_URI];
+       while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
+               if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
+                       mg_snprintf(conn,
+                                   &truncated,
+                                   fname,
+                                   sizeof(fname),
+                                   "%.*s",
+                                   (int)filename_vec.len,
+                                   filename_vec.ptr);
+
+                       if (truncated || !mg_fopen(conn, fname, "r", &file)) {
+                               mg_cry(conn,
+                                      "%s: cannot open %s: %s",
+                                      __func__,
+                                      fname,
+                                      strerror(errno));
+                       }
+                       break;
+               }
+       }
+
+       if (!is_file_opened(&file)) {
+               open_auth_file(conn, path, &file);
+       }
+
+       if (is_file_opened(&file)) {
+               authorized = authorize(conn, &file);
+               mg_fclose(&file);
+       }
+
+       return authorized;
+}
+
+
+static void
+send_authorization_request(struct mg_connection *conn)
+{
+       char date[64];
+       time_t curtime = time(NULL);
+
+       if (conn && conn->ctx) {
+               uint64_t nonce = (uint64_t)(conn->ctx->start_time);
+
+               (void)pthread_mutex_lock(&conn->ctx->nonce_mutex);
+               nonce += conn->ctx->nonce_count;
+               ++conn->ctx->nonce_count;
+               (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex);
+
+               nonce ^= conn->ctx->auth_nonce_mask;
+               conn->status_code = 401;
+               conn->must_close = 1;
+
+               gmt_time_string(date, sizeof(date), &curtime);
+
+               mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n");
+               send_no_cache_header(conn);
+               mg_printf(conn,
+                         "Date: %s\r\n"
+                         "Connection: %s\r\n"
+                         "Content-Length: 0\r\n"
+                         "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
+                         "nonce=\"%" UINT64_FMT "\"\r\n\r\n",
+                         date,
+                         suggest_connection_header(conn),
+                         conn->ctx->config[AUTHENTICATION_DOMAIN],
+                         nonce);
+       }
+}
+
+
+#if !defined(NO_FILES)
+static int
+is_authorized_for_put(struct mg_connection *conn)
+{
+       if (conn) {
+               struct file file = STRUCT_FILE_INITIALIZER;
+               const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
+               int ret = 0;
+
+               if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) {
+                       ret = authorize(conn, &file);
+                       mg_fclose(&file);
+               }
+
+               return ret;
+       }
+       return 0;
+}
+#endif
+
+
+int
+mg_modify_passwords_file(const char *fname,
+                         const char *domain,
+                         const char *user,
+                         const char *pass)
+{
+       int found, i;
+       char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8];
+       FILE *fp, *fp2;
+
+       found = 0;
+       fp = fp2 = NULL;
+
+       /* Regard empty password as no password - remove user record. */
+       if (pass != NULL && pass[0] == '\0') {
+               pass = NULL;
+       }
+
+       /* Other arguments must not be empty */
+       if (fname == NULL || domain == NULL || user == NULL) {
+               return 0;
+       }
+
+       /* Using the given file format, user name and domain must not contain ':'
+        */
+       if (strchr(user, ':') != NULL) {
+               return 0;
+       }
+       if (strchr(domain, ':') != NULL) {
+               return 0;
+       }
+
+       /* Do not allow control characters like newline in user name and domain.
+        * Do not allow excessively long names either. */
+       for (i = 0; i < 255 && user[i] != 0; i++) {
+               if (iscntrl(user[i])) {
+                       return 0;
+               }
+       }
+       if (user[i]) {
+               return 0;
+       }
+       for (i = 0; i < 255 && domain[i] != 0; i++) {
+               if (iscntrl(domain[i])) {
+                       return 0;
+               }
+       }
+       if (domain[i]) {
+               return 0;
+       }
+
+       /* The maximum length of the path to the password file is limited */
+       if ((strlen(fname) + 4) >= PATH_MAX) {
+               return 0;
+       }
+
+       /* Create a temporary file name. Length has been checked before. */
+       strcpy(tmp, fname);
+       strcat(tmp, ".tmp");
+
+       /* Create the file if does not exist */
+       /* Use of fopen here is OK, since fname is only ASCII */
+       if ((fp = fopen(fname, "a+")) != NULL) {
+               (void)fclose(fp);
+       }
+
+       /* Open the given file and temporary file */
+       if ((fp = fopen(fname, "r")) == NULL) {
+               return 0;
+       } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
+               fclose(fp);
+               return 0;
+       }
+
+       /* Copy the stuff to temporary file */
+       while (fgets(line, sizeof(line), fp) != NULL) {
+               if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) {
+                       continue;
+               }
+               u[255] = 0;
+               d[255] = 0;
+
+               if (!strcmp(u, user) && !strcmp(d, domain)) {
+                       found++;
+                       if (pass != NULL) {
+                               mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+                               fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+                       }
+               } else {
+                       fprintf(fp2, "%s", line);
+               }
+       }
+
+       /* If new user, just add it */
+       if (!found && pass != NULL) {
+               mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+               fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+       }
+
+       /* Close files */
+       fclose(fp);
+       fclose(fp2);
+
+       /* Put the temp file in place of real file */
+       IGNORE_UNUSED_RESULT(remove(fname));
+       IGNORE_UNUSED_RESULT(rename(tmp, fname));
+
+       return 1;
+}
+
+
+static int
+is_valid_port(unsigned long port)
+{
+       return port < 0xffff;
+}
+
+
+static int
+mg_inet_pton(int af, const char *src, void *dst, size_t dstlen)
+{
+       struct addrinfo hints, *res, *ressave;
+       int func_ret = 0;
+       int gai_ret;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = af;
+
+       gai_ret = getaddrinfo(src, NULL, &hints, &res);
+       if (gai_ret != 0) {
+               /* gai_strerror could be used to convert gai_ret to a string */
+               /* POSIX return values: see
+                * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
+                */
+               /* Windows return values: see
+                * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
+                */
+               return 0;
+       }
+
+       ressave = res;
+
+       while (res) {
+               if (dstlen >= res->ai_addrlen) {
+                       memcpy(dst, res->ai_addr, res->ai_addrlen);
+                       func_ret = 1;
+               }
+               res = res->ai_next;
+       }
+
+       freeaddrinfo(ressave);
+       return func_ret;
+}
+
+
+static int
+connect_socket(struct mg_context *ctx /* may be NULL */,
+               const char *host,
+               int port,
+               int use_ssl,
+               char *ebuf,
+               size_t ebuf_len,
+               SOCKET *sock /* output: socket, must not be NULL */,
+               union usa *sa /* output: socket address, must not be NULL  */
+               )
+{
+       int ip_ver = 0;
+       *sock = INVALID_SOCKET;
+       memset(sa, 0, sizeof(*sa));
+
+       if (ebuf_len > 0) {
+               *ebuf = 0;
+       }
+
+       if (host == NULL) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "NULL host");
+               return 0;
+       }
+
+       if (port < 0 || !is_valid_port((unsigned)port)) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "invalid port");
+               return 0;
+       }
+
+       if (use_ssl && (SSLv23_client_method == NULL)) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "SSL is not initialized");
+               return 0;
+       }
+
+       if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin))) {
+               sa->sin.sin_port = htons((uint16_t)port);
+               ip_ver = 4;
+#ifdef USE_IPV6
+       } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6))) {
+               sa->sin6.sin6_port = htons((uint16_t)port);
+               ip_ver = 6;
+       } else if (host[0] == '[') {
+               /* While getaddrinfo on Windows will work with [::1],
+                * getaddrinfo on Linux only works with ::1 (without []). */
+               size_t l = strlen(host + 1);
+               char *h = l > 1 ? mg_strdup(host + 1) : NULL;
+               if (h) {
+                       h[l - 1] = 0;
+                       if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) {
+                               sa->sin6.sin6_port = htons((uint16_t)port);
+                               ip_ver = 6;
+                       }
+                       mg_free(h);
+               }
+#endif
+       }
+
+       if (ip_ver == 0) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "host not found");
+               return 0;
+       }
+
+       if (ip_ver == 4) {
+               *sock = socket(PF_INET, SOCK_STREAM, 0);
+       }
+#ifdef USE_IPV6
+       else if (ip_ver == 6) {
+               *sock = socket(PF_INET6, SOCK_STREAM, 0);
+       }
+#endif
+
+       if (*sock == INVALID_SOCKET) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "socket(): %s",
+                           strerror(ERRNO));
+               return 0;
+       }
+
+       set_close_on_exec(*sock, fc(ctx));
+
+       if ((ip_ver == 4)
+           && (connect(*sock, (struct sockaddr *)&sa->sin, sizeof(sa->sin))
+               == 0)) {
+               /* connected with IPv4 */
+               return 1;
+       }
+
+#ifdef USE_IPV6
+       if ((ip_ver == 6)
+           && (connect(*sock, (struct sockaddr *)&sa->sin6, sizeof(sa->sin6))
+               == 0)) {
+               /* connected with IPv6 */
+               return 1;
+       }
+#endif
+
+       /* Not connected */
+       mg_snprintf(NULL,
+                   NULL, /* No truncation check for ebuf */
+                   ebuf,
+                   ebuf_len,
+                   "connect(%s:%d): %s",
+                   host,
+                   port,
+                   strerror(ERRNO));
+       closesocket(*sock);
+       *sock = INVALID_SOCKET;
+       return 0;
+}
+
+
+int
+mg_url_encode(const char *src, char *dst, size_t dst_len)
+{
+       static const char *dont_escape = "._-$,;~()";
+       static const char *hex = "0123456789abcdef";
+       char *pos = dst;
+       const char *end = dst + dst_len - 1;
+
+       for (; *src != '\0' && pos < end; src++, pos++) {
+               if (isalnum(*(const unsigned char *)src)
+                   || strchr(dont_escape, *(const unsigned char *)src) != NULL) {
+                       *pos = *src;
+               } else if (pos + 2 < end) {
+                       pos[0] = '%';
+                       pos[1] = hex[(*(const unsigned char *)src) >> 4];
+                       pos[2] = hex[(*(const unsigned char *)src) & 0xf];
+                       pos += 2;
+               } else {
+                       break;
+               }
+       }
+
+       *pos = '\0';
+       return (*src == '\0') ? (int)(pos - dst) : -1;
+}
+
+
+static void
+print_dir_entry(struct de *de)
+{
+       char size[64], mod[64], href[PATH_MAX];
+       struct tm *tm;
+
+       if (de->file.is_directory) {
+               mg_snprintf(de->conn,
+                           NULL, /* Buffer is big enough */
+                           size,
+                           sizeof(size),
+                           "%s",
+                           "[DIRECTORY]");
+       } else {
+               /* We use (signed) cast below because MSVC 6 compiler cannot
+                * convert unsigned __int64 to double. Sigh. */
+               if (de->file.size < 1024) {
+                       mg_snprintf(de->conn,
+                                   NULL, /* Buffer is big enough */
+                                   size,
+                                   sizeof(size),
+                                   "%d",
+                                   (int)de->file.size);
+               } else if (de->file.size < 0x100000) {
+                       mg_snprintf(de->conn,
+                                   NULL, /* Buffer is big enough */
+                                   size,
+                                   sizeof(size),
+                                   "%.1fk",
+                                   (double)de->file.size / 1024.0);
+               } else if (de->file.size < 0x40000000) {
+                       mg_snprintf(de->conn,
+                                   NULL, /* Buffer is big enough */
+                                   size,
+                                   sizeof(size),
+                                   "%.1fM",
+                                   (double)de->file.size / 1048576);
+               } else {
+                       mg_snprintf(de->conn,
+                                   NULL, /* Buffer is big enough */
+                                   size,
+                                   sizeof(size),
+                                   "%.1fG",
+                                   (double)de->file.size / 1073741824);
+               }
+       }
+
+       /* Note: mg_snprintf will not cause a buffer overflow above.
+        * So, string truncation checks are not required here. */
+
+       tm = localtime(&de->file.last_modified);
+       if (tm != NULL) {
+               strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
+       } else {
+               mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
+               mod[sizeof(mod) - 1] = '\0';
+       }
+       mg_url_encode(de->file_name, href, sizeof(href));
+       de->conn->num_bytes_sent +=
+           mg_printf(de->conn,
+                     "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+                     "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+                     de->conn->request_info.local_uri,
+                     href,
+                     de->file.is_directory ? "/" : "",
+                     de->file_name,
+                     de->file.is_directory ? "/" : "",
+                     mod,
+                     size);
+}
+
+
+/* This function is called from send_directory() and used for
+ * sorting directory entries by size, or name, or modification time.
+ * On windows, __cdecl specification is needed in case if project is built
+ * with __stdcall convention. qsort always requires __cdels callback. */
+static int WINCDECL
+compare_dir_entries(const void *p1, const void *p2)
+{
+       if (p1 && p2) {
+               const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
+               const char *query_string = a->conn->request_info.query_string;
+               int cmp_result = 0;
+
+               if (query_string == NULL) {
+                       query_string = "na";
+               }
+
+               if (a->file.is_directory && !b->file.is_directory) {
+                       return -1; /* Always put directories on top */
+               } else if (!a->file.is_directory && b->file.is_directory) {
+                       return 1; /* Always put directories on top */
+               } else if (*query_string == 'n') {
+                       cmp_result = strcmp(a->file_name, b->file_name);
+               } else if (*query_string == 's') {
+                       cmp_result = a->file.size == b->file.size
+                                        ? 0
+                                        : a->file.size > b->file.size ? 1 : -1;
+               } else if (*query_string == 'd') {
+                       cmp_result =
+                           (a->file.last_modified == b->file.last_modified)
+                               ? 0
+                               : ((a->file.last_modified > b->file.last_modified) ? 1
+                                                                                  : -1);
+               }
+
+               return query_string[1] == 'd' ? -cmp_result : cmp_result;
+       }
+       return 0;
+}
+
+
+static int
+must_hide_file(struct mg_connection *conn, const char *path)
+{
+       if (conn && conn->ctx) {
+               const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
+               const char *pattern = conn->ctx->config[HIDE_FILES];
+               return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0
+                      || (pattern != NULL
+                          && match_prefix(pattern, strlen(pattern), path) > 0);
+       }
+       return 0;
+}
+
+
+static int
+scan_directory(struct mg_connection *conn,
+               const char *dir,
+               void *data,
+               void (*cb)(struct de *, void *))
+{
+       char path[PATH_MAX];
+       struct dirent *dp;
+       DIR *dirp;
+       struct de de;
+       int truncated;
+
+       if ((dirp = mg_opendir(conn, dir)) == NULL) {
+               return 0;
+       } else {
+               de.conn = conn;
+
+               while ((dp = mg_readdir(dirp)) != NULL) {
+                       /* Do not show current dir and hidden files */
+                       if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
+                           || must_hide_file(conn, dp->d_name)) {
+                               continue;
+                       }
+
+                       mg_snprintf(
+                           conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
+
+                       /* If we don't memset stat structure to zero, mtime will have
+                        * garbage and strftime() will segfault later on in
+                        * print_dir_entry(). memset is required only if mg_stat()
+                        * fails. For more details, see
+                        * http://code.google.com/p/mongoose/issues/detail?id=79 */
+                       memset(&de.file, 0, sizeof(de.file));
+
+                       if (truncated) {
+                               /* If the path is not complete, skip processing. */
+                               continue;
+                       }
+
+                       if (!mg_stat(conn, path, &de.file)) {
+                               mg_cry(conn,
+                                      "%s: mg_stat(%s) failed: %s",
+                                      __func__,
+                                      path,
+                                      strerror(ERRNO));
+                       }
+                       de.file_name = dp->d_name;
+                       cb(&de, data);
+               }
+               (void)mg_closedir(dirp);
+       }
+       return 1;
+}
+
+
+#if !defined(NO_FILES)
+static int
+remove_directory(struct mg_connection *conn, const char *dir)
+{
+       char path[PATH_MAX];
+       struct dirent *dp;
+       DIR *dirp;
+       struct de de;
+       int truncated;
+       int ok = 1;
+
+       if ((dirp = mg_opendir(conn, dir)) == NULL) {
+               return 0;
+       } else {
+               de.conn = conn;
+
+               while ((dp = mg_readdir(dirp)) != NULL) {
+                       /* Do not show current dir (but show hidden files as they will
+                        * also be removed) */
+                       if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
+                               continue;
+                       }
+
+                       mg_snprintf(
+                           conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
+
+                       /* If we don't memset stat structure to zero, mtime will have
+                        * garbage and strftime() will segfault later on in
+                        * print_dir_entry(). memset is required only if mg_stat()
+                        * fails. For more details, see
+                        * http://code.google.com/p/mongoose/issues/detail?id=79 */
+                       memset(&de.file, 0, sizeof(de.file));
+
+                       if (truncated) {
+                               /* Do not delete anything shorter */
+                               ok = 0;
+                               continue;
+                       }
+
+                       if (!mg_stat(conn, path, &de.file)) {
+                               mg_cry(conn,
+                                      "%s: mg_stat(%s) failed: %s",
+                                      __func__,
+                                      path,
+                                      strerror(ERRNO));
+                               ok = 0;
+                       }
+                       if (de.file.membuf == NULL) {
+                               /* file is not in memory */
+                               if (de.file.is_directory) {
+                                       if (remove_directory(conn, path) == 0) {
+                                               ok = 0;
+                                       }
+                               } else {
+                                       if (mg_remove(conn, path) == 0) {
+                                               ok = 0;
+                                       }
+                               }
+                       } else {
+                               /* file is in memory. It can not be deleted. */
+                               ok = 0;
+                       }
+               }
+               (void)mg_closedir(dirp);
+
+               IGNORE_UNUSED_RESULT(rmdir(dir));
+       }
+
+       return ok;
+}
+#endif
+
+
+struct dir_scan_data {
+       struct de *entries;
+       unsigned int num_entries;
+       unsigned int arr_size;
+};
+
+
+/* Behaves like realloc(), but frees original pointer on failure */
+static void *
+realloc2(void *ptr, size_t size)
+{
+       void *new_ptr = mg_realloc(ptr, size);
+       if (new_ptr == NULL) {
+               mg_free(ptr);
+       }
+       return new_ptr;
+}
+
+
+static void
+dir_scan_callback(struct de *de, void *data)
+{
+       struct dir_scan_data *dsd = (struct dir_scan_data *)data;
+
+       if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
+               dsd->arr_size *= 2;
+               dsd->entries =
+                   (struct de *)realloc2(dsd->entries,
+                                         dsd->arr_size * sizeof(dsd->entries[0]));
+       }
+       if (dsd->entries == NULL) {
+               /* TODO(lsm, low): propagate an error to the caller */
+               dsd->num_entries = 0;
+       } else {
+               dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
+               dsd->entries[dsd->num_entries].file = de->file;
+               dsd->entries[dsd->num_entries].conn = de->conn;
+               dsd->num_entries++;
+       }
+}
+
+
+static void
+handle_directory_request(struct mg_connection *conn, const char *dir)
+{
+       unsigned int i;
+       int sort_direction;
+       struct dir_scan_data data = {NULL, 0, 128};
+       char date[64];
+       time_t curtime = time(NULL);
+
+       if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
+               send_http_error(conn,
+                               500,
+                               "Error: Cannot open directory\nopendir(%s): %s",
+                               dir,
+                               strerror(ERRNO));
+               return;
+       }
+
+       gmt_time_string(date, sizeof(date), &curtime);
+
+       if (!conn) {
+               return;
+       }
+
+       sort_direction = conn->request_info.query_string != NULL
+                                && conn->request_info.query_string[1] == 'd'
+                            ? 'a'
+                            : 'd';
+
+       conn->must_close = 1;
+       mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+       send_static_cache_header(conn);
+       mg_printf(conn,
+                 "Date: %s\r\n"
+                 "Connection: close\r\n"
+                 "Content-Type: text/html; charset=utf-8\r\n\r\n",
+                 date);
+
+       conn->num_bytes_sent +=
+           mg_printf(conn,
+                     "<html><head><title>Index of %s</title>"
+                     "<style>th {text-align: left;}</style></head>"
+                     "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
+                     "<tr><th><a href=\"?n%c\">Name</a></th>"
+                     "<th><a href=\"?d%c\">Modified</a></th>"
+                     "<th><a href=\"?s%c\">Size</a></th></tr>"
+                     "<tr><td colspan=\"3\"><hr></td></tr>",
+                     conn->request_info.local_uri,
+                     conn->request_info.local_uri,
+                     sort_direction,
+                     sort_direction,
+                     sort_direction);
+
+       /* Print first entry - link to a parent directory */
+       conn->num_bytes_sent +=
+           mg_printf(conn,
+                     "<tr><td><a href=\"%s%s\">%s</a></td>"
+                     "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+                     conn->request_info.local_uri,
+                     "..",
+                     "Parent directory",
+                     "-",
+                     "-");
+
+       /* Sort and print directory entries */
+       if (data.entries != NULL) {
+               qsort(data.entries,
+                     (size_t)data.num_entries,
+                     sizeof(data.entries[0]),
+                     compare_dir_entries);
+               for (i = 0; i < data.num_entries; i++) {
+                       print_dir_entry(&data.entries[i]);
+                       mg_free(data.entries[i].file_name);
+               }
+               mg_free(data.entries);
+       }
+
+       conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
+       conn->status_code = 200;
+}
+
+
+/* Send len bytes from the opened file to the client. */
+static void
+send_file_data(struct mg_connection *conn,
+               struct file *filep,
+               int64_t offset,
+               int64_t len)
+{
+       char buf[MG_BUF_LEN];
+       int to_read, num_read, num_written;
+       int64_t size;
+
+       if (!filep || !conn) {
+               return;
+       }
+
+       /* Sanity check the offset */
+       size = filep->size > INT64_MAX ? INT64_MAX : (int64_t)(filep->size);
+       offset = offset < 0 ? 0 : offset > size ? size : offset;
+
+       if (len > 0 && filep->membuf != NULL && size > 0) {
+               /* file stored in memory */
+               if (len > size - offset) {
+                       len = size - offset;
+               }
+               mg_write(conn, filep->membuf + offset, (size_t)len);
+       } else if (len > 0 && filep->fp != NULL) {
+/* file stored on disk */
+#if defined(__linux__)
+               /* sendfile is only available for Linux */
+               if (conn->throttle == 0 && conn->ssl == 0) {
+                       off_t sf_offs = (off_t)offset;
+                       ssize_t sf_sent;
+                       int sf_file = fileno(filep->fp);
+                       int loop_cnt = 0;
+
+                       do {
+                               /* 2147479552 (0x7FFFF000) is a limit found by experiment on
+                                * 64 bit Linux (2^31 minus one memory page of 4k?). */
+                               size_t sf_tosend =
+                                   (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
+                               sf_sent =
+                                   sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
+                               if (sf_sent > 0) {
+                                       conn->num_bytes_sent += sf_sent;
+                                       len -= sf_sent;
+                                       offset += sf_sent;
+                               } else if (loop_cnt == 0) {
+                                       /* This file can not be sent using sendfile.
+                                        * This might be the case for pseudo-files in the
+                                        * /sys/ and /proc/ file system.
+                                        * Use the regular user mode copy code instead. */
+                                       break;
+                               } else if (sf_sent == 0) {
+                                       /* No error, but 0 bytes sent. May be EOF? */
+                                       return;
+                               }
+                               loop_cnt++;
+
+                       } while ((len > 0) && (sf_sent >= 0));
+
+                       if (sf_sent > 0) {
+                               return; /* OK */
+                       }
+
+                       /* sf_sent<0 means error, thus fall back to the classic way */
+                       /* This is always the case, if sf_file is not a "normal" file,
+                        * e.g., for sending data from the output of a CGI process. */
+                       offset = (int64_t)sf_offs;
+               }
+#endif
+               if ((offset > 0) && (fseeko(filep->fp, offset, SEEK_SET) != 0)) {
+                       mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO));
+                       send_http_error(
+                           conn,
+                           500,
+                           "%s",
+                           "Error: Unable to access file at requested position.");
+               } else {
+                       while (len > 0) {
+                               /* Calculate how much to read from the file in the buffer */
+                               to_read = sizeof(buf);
+                               if ((int64_t)to_read > len) {
+                                       to_read = (int)len;
+                               }
+
+                               /* Read from file, exit the loop on error */
+                               if ((num_read = (int)fread(buf, 1, (size_t)to_read, filep->fp))
+                                   <= 0) {
+                                       break;
+                               }
+
+                               /* Send read bytes to the client, exit the loop on error */
+                               if ((num_written = mg_write(conn, buf, (size_t)num_read))
+                                   != num_read) {
+                                       break;
+                               }
+
+                               /* Both read and were successful, adjust counters */
+                               conn->num_bytes_sent += num_written;
+                               len -= num_written;
+                       }
+               }
+       }
+}
+
+
+static int
+parse_range_header(const char *header, int64_t *a, int64_t *b)
+{
+       return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
+}
+
+
+static void
+construct_etag(char *buf, size_t buf_len, const struct file *filep)
+{
+       if (filep != NULL && buf != NULL) {
+               mg_snprintf(NULL,
+                           NULL, /* All calls to construct_etag use 64 byte buffer */
+                           buf,
+                           buf_len,
+                           "\"%lx.%" INT64_FMT "\"",
+                           (unsigned long)filep->last_modified,
+                           filep->size);
+       }
+}
+
+
+static void
+fclose_on_exec(struct file *filep, struct mg_connection *conn)
+{
+       if (filep != NULL && filep->fp != NULL) {
+#ifdef _WIN32
+               (void)conn; /* Unused. */
+#else
+               if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
+                       mg_cry(conn,
+                              "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
+                              __func__,
+                              strerror(ERRNO));
+               }
+#endif
+       }
+}
+
+
+static void
+handle_static_file_request(struct mg_connection *conn,
+                           const char *path,
+                           struct file *filep,
+                           const char *mime_type)
+{
+       char date[64], lm[64], etag[64];
+       char range[128]; /* large enough, so there will be no overflow */
+       const char *msg = "OK", *hdr;
+       time_t curtime = time(NULL);
+       int64_t cl, r1, r2;
+       struct vec mime_vec;
+       int n, truncated;
+       char gz_path[PATH_MAX];
+       const char *encoding = "";
+       const char *cors1, *cors2, *cors3;
+
+       if (conn == NULL || conn->ctx == NULL || filep == NULL) {
+               return;
+       }
+
+       if (mime_type == NULL) {
+               get_mime_type(conn->ctx, path, &mime_vec);
+       } else {
+               mime_vec.ptr = mime_type;
+               mime_vec.len = strlen(mime_type);
+       }
+       if (filep->size > INT64_MAX) {
+               send_http_error(conn,
+                               500,
+                               "Error: File size is too large to send\n%" INT64_FMT,
+                               filep->size);
+       }
+       cl = (int64_t)filep->size;
+       conn->status_code = 200;
+       range[0] = '\0';
+
+       /* if this file is in fact a pre-gzipped file, rewrite its filename
+        * it's important to rewrite the filename after resolving
+        * the mime type from it, to preserve the actual file's type */
+       if (filep->gzipped) {
+               mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
+
+               if (truncated) {
+                       send_http_error(conn,
+                                       500,
+                                       "Error: Path of zipped file too long (%s)",
+                                       path);
+                       return;
+               }
+
+               path = gz_path;
+               encoding = "Content-Encoding: gzip\r\n";
+       }
+
+       if (!mg_fopen(conn, path, "rb", filep)) {
+               send_http_error(conn,
+                               500,
+                               "Error: Cannot open file\nfopen(%s): %s",
+                               path,
+                               strerror(ERRNO));
+               return;
+       }
+
+       fclose_on_exec(filep, conn);
+
+       /* If Range: header specified, act accordingly */
+       r1 = r2 = 0;
+       hdr = mg_get_header(conn, "Range");
+       if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0
+           && r2 >= 0) {
+               /* actually, range requests don't play well with a pre-gzipped
+                * file (since the range is specified in the uncompressed space) */
+               if (filep->gzipped) {
+                       send_http_error(
+                           conn,
+                           501,
+                           "%s",
+                           "Error: Range requests in gzipped files are not supported");
+                       mg_fclose(filep);
+                       return;
+               }
+               conn->status_code = 206;
+               cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1;
+               mg_snprintf(conn,
+                           NULL, /* range buffer is big enough */
+                           range,
+                           sizeof(range),
+                           "Content-Range: bytes "
+                           "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
+                           r1,
+                           r1 + cl - 1,
+                           filep->size);
+               msg = "Partial Content";
+       }
+
+       hdr = mg_get_header(conn, "Origin");
+       if (hdr) {
+               /* Cross-origin resource sharing (CORS), see
+                * http://www.html5rocks.com/en/tutorials/cors/,
+                * http://www.html5rocks.com/static/images/cors_server_flowchart.png -
+                * preflight is not supported for files. */
+               cors1 = "Access-Control-Allow-Origin: ";
+               cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
+               cors3 = "\r\n";
+       } else {
+               cors1 = cors2 = cors3 = "";
+       }
+
+       /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
+        * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */
+       gmt_time_string(date, sizeof(date), &curtime);
+       gmt_time_string(lm, sizeof(lm), &filep->last_modified);
+       construct_etag(etag, sizeof(etag), filep);
+
+       (void)mg_printf(conn,
+                       "HTTP/1.1 %d %s\r\n"
+                       "%s%s%s"
+                       "Date: %s\r\n",
+                       conn->status_code,
+                       msg,
+                       cors1,
+                       cors2,
+                       cors3,
+                       date);
+       send_static_cache_header(conn);
+       (void)mg_printf(conn,
+                       "Last-Modified: %s\r\n"
+                       "Etag: %s\r\n"
+                       "Content-Type: %.*s\r\n"
+                       "Content-Length: %" INT64_FMT "\r\n"
+                       "Connection: %s\r\n"
+                       "Accept-Ranges: bytes\r\n"
+                       "%s%s\r\n",
+                       lm,
+                       etag,
+                       (int)mime_vec.len,
+                       mime_vec.ptr,
+                       cl,
+                       suggest_connection_header(conn),
+                       range,
+                       encoding);
+
+       if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
+               send_file_data(conn, filep, r1, cl);
+       }
+       mg_fclose(filep);
+}
+
+
+void
+mg_send_file(struct mg_connection *conn, const char *path)
+{
+       mg_send_mime_file(conn, path, NULL);
+}
+
+
+void
+mg_send_mime_file(struct mg_connection *conn,
+                  const char *path,
+                  const char *mime_type)
+{
+       struct file file = STRUCT_FILE_INITIALIZER;
+       if (mg_stat(conn, path, &file)) {
+               if (file.is_directory) {
+                       if (!conn) {
+                               return;
+                       }
+                       if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING],
+                                          "yes")) {
+                               handle_directory_request(conn, path);
+                       } else {
+                               send_http_error(conn,
+                                               403,
+                                               "%s",
+                                               "Error: Directory listing denied");
+                       }
+               } else {
+                       handle_static_file_request(conn, path, &file, mime_type);
+               }
+       } else {
+               send_http_error(conn, 404, "%s", "Error: File not found");
+       }
+}
+
+
+/* For a given PUT path, create all intermediate subdirectories.
+ * Return  0  if the path itself is a directory.
+ * Return  1  if the path leads to a file.
+ * Return -1  for if the path is too long.
+ * Return -2  if path can not be created.
+*/
+static int
+put_dir(struct mg_connection *conn, const char *path)
+{
+       char buf[PATH_MAX];
+       const char *s, *p;
+       struct file file = STRUCT_FILE_INITIALIZER;
+       size_t len;
+       int res = 1;
+
+       for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
+               len = (size_t)(p - path);
+               if (len >= sizeof(buf)) {
+                       /* path too long */
+                       res = -1;
+                       break;
+               }
+               memcpy(buf, path, len);
+               buf[len] = '\0';
+
+               /* Try to create intermediate directory */
+               DEBUG_TRACE("mkdir(%s)", buf);
+               if (!mg_stat(conn, buf, &file) && mg_mkdir(conn, buf, 0755) != 0) {
+                       /* path does not exixt and can not be created */
+                       res = -2;
+                       break;
+               }
+
+               /* Is path itself a directory? */
+               if (p[1] == '\0') {
+                       res = 0;
+               }
+       }
+
+       return res;
+}
+
+
+static void
+remove_bad_file(const struct mg_connection *conn, const char *path)
+{
+       int r = mg_remove(conn, path);
+       if (r != 0) {
+               mg_cry(conn, "%s: Cannot remove invalid file %s", __func__, path);
+       }
+}
+
+
+long long
+mg_store_body(struct mg_connection *conn, const char *path)
+{
+       char buf[MG_BUF_LEN];
+       long long len = 0;
+       int ret, n;
+       struct file fi;
+
+       if (conn->consumed_content != 0) {
+               mg_cry(conn, "%s: Contents already consumed", __func__);
+               return -11;
+       }
+
+       ret = put_dir(conn, path);
+       if (ret < 0) {
+               /* -1 for path too long,
+                * -2 for path can not be created. */
+               return ret;
+       }
+       if (ret != 1) {
+               /* Return 0 means, path itself is a directory. */
+               return 0;
+       }
+
+       if (mg_fopen(conn, path, "w", &fi) == 0) {
+               return -12;
+       }
+
+       ret = mg_read(conn, buf, sizeof(buf));
+       while (ret > 0) {
+               n = (int)fwrite(buf, 1, (size_t)ret, fi.fp);
+               if (n != ret) {
+                       mg_fclose(&fi);
+                       remove_bad_file(conn, path);
+                       return -13;
+               }
+               ret = mg_read(conn, buf, sizeof(buf));
+       }
+
+       /* TODO: mg_fclose should return an error,
+        * and every caller should check and handle it. */
+       if (fclose(fi.fp) != 0) {
+               remove_bad_file(conn, path);
+               return -14;
+       }
+
+       return len;
+}
+
+
+/* Parse HTTP headers from the given buffer, advance buffer to the point
+ * where parsing stopped. */
+static void
+parse_http_headers(char **buf, struct mg_request_info *ri)
+{
+       int i;
+
+       if (!ri) {
+               return;
+       }
+
+       ri->num_headers = 0;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) {
+               char *dp = *buf;
+               while ((*dp != ':') && (*dp != '\r') && (*dp != 0)) {
+                       dp++;
+               }
+               if (!*dp) {
+                       /* neither : nor \r\n. This is not a valid field. */
+                       break;
+               }
+               if (*dp == '\r') {
+                       if (dp[1] == '\n') {
+                               /* \r\n */
+                               ri->http_headers[i].name = *buf;
+                               ri->http_headers[i].value = 0;
+                               *buf = dp;
+                       } else {
+                               /* stray \r. This is not valid. */
+                               break;
+                       }
+               } else {
+                       /* (*dp == ':') */
+                       *dp = 0;
+                       ri->http_headers[i].name = *buf;
+                       do {
+                               dp++;
+                       } while (*dp == ' ');
+
+                       ri->http_headers[i].value = dp;
+                       *buf = strstr(dp, "\r\n");
+               }
+
+               ri->num_headers = i + 1;
+               if (*buf) {
+                       (*buf)[0] = 0;
+                       (*buf)[1] = 0;
+                       *buf += 2;
+               } else {
+                       *buf = dp;
+                       break;
+               }
+
+               if (*buf[0] == '\r') {
+                       /* This is the end of the header */
+                       break;
+               }
+       }
+}
+
+
+static int
+is_valid_http_method(const char *method)
+{
+       return !strcmp(method, "GET")        /* HTTP (RFC 2616) */
+              || !strcmp(method, "POST")    /* HTTP (RFC 2616) */
+              || !strcmp(method, "HEAD")    /* HTTP (RFC 2616) */
+              || !strcmp(method, "PUT")     /* HTTP (RFC 2616) */
+              || !strcmp(method, "DELETE")  /* HTTP (RFC 2616) */
+              || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */
+              /* TRACE method (RFC 2616) is not supported for security reasons */
+              || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */
+
+              || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */
+              || !strcmp(method, "MKCOL")    /* WEBDAV (RFC 2518) */
+
+              /* Unsupported WEBDAV Methods: */
+              /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
+              /* + 11 methods from RFC 3253 */
+              /* ORDERPATCH (RFC 3648) */
+              /* ACL (RFC 3744) */
+              /* SEARCH (RFC 5323) */
+              /* + MicroSoft extensions
+               * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
+
+              /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
+              || !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */
+}
+
+
+/* Parse HTTP request, fill in mg_request_info structure.
+ * This function modifies the buffer by NUL-terminating
+ * HTTP request components, header names and header values. */
+static int
+parse_http_message(char *buf, int len, struct mg_request_info *ri)
+{
+       int is_request, request_length;
+
+       if (!ri) {
+               return 0;
+       }
+
+       request_length = get_request_len(buf, len);
+
+       if (request_length > 0) {
+               /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr,
+                * remote_port */
+               ri->remote_user = ri->request_method = ri->request_uri =
+                   ri->http_version = NULL;
+               ri->num_headers = 0;
+
+               buf[request_length - 1] = '\0';
+
+               /* RFC says that all initial whitespaces should be ingored */
+               while (*buf != '\0' && isspace(*(unsigned char *)buf)) {
+                       buf++;
+               }
+               ri->request_method = skip(&buf, " ");
+               ri->request_uri = skip(&buf, " ");
+               ri->http_version = skip(&buf, "\r\n");
+
+               /* HTTP message could be either HTTP request or HTTP response, e.g.
+                * "GET / HTTP/1.0 ...." or  "HTTP/1.0 200 OK ..." */
+               is_request = is_valid_http_method(ri->request_method);
+               if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0)
+                   || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
+                       request_length = -1;
+               } else {
+                       if (is_request) {
+                               ri->http_version += 5;
+                       }
+                       parse_http_headers(&buf, ri);
+               }
+       }
+       return request_length;
+}
+
+
+/* Keep reading the input (either opened file descriptor fd, or socket sock,
+ * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
+ * buffer (which marks the end of HTTP request). Buffer buf may already
+ * have some data. The length of the data is stored in nread.
+ * Upon every read operation, increase nread by the number of bytes read. */
+static int
+read_request(FILE *fp,
+             struct mg_connection *conn,
+             char *buf,
+             int bufsiz,
+             int *nread)
+{
+       int request_len, n = 0;
+       struct timespec last_action_time;
+       double request_timeout;
+
+       if (!conn) {
+               return 0;
+       }
+
+       memset(&last_action_time, 0, sizeof(last_action_time));
+
+       if (conn->ctx->config[REQUEST_TIMEOUT]) {
+               /* value of request_timeout is in seconds, config in milliseconds */
+               request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+       } else {
+               request_timeout = -1.0;
+       }
+
+       request_len = get_request_len(buf, *nread);
+
+       /* first time reading from this connection */
+       clock_gettime(CLOCK_MONOTONIC, &last_action_time);
+
+       while (
+           (conn->ctx->stop_flag == 0) && (*nread < bufsiz) && (request_len == 0)
+           && ((mg_difftimespec(&last_action_time, &(conn->req_time))
+                <= request_timeout) || (request_timeout < 0))
+           && ((n = pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout))
+               > 0)) {
+               *nread += n;
+               /* assert(*nread <= bufsiz); */
+               if (*nread > bufsiz) {
+                       return -2;
+               }
+               request_len = get_request_len(buf, *nread);
+               if (request_timeout > 0.0) {
+                       clock_gettime(CLOCK_MONOTONIC, &last_action_time);
+               }
+       }
+
+       return (request_len <= 0 && n <= 0) ? -1 : request_len;
+}
+
+#if !defined(NO_FILES)
+/* For given directory path, substitute it to valid index file.
+ * Return 1 if index file has been found, 0 if not found.
+ * If the file is found, it's stats is returned in stp. */
+static int
+substitute_index_file(struct mg_connection *conn,
+                      char *path,
+                      size_t path_len,
+                      struct file *filep)
+{
+       if (conn && conn->ctx) {
+               const char *list = conn->ctx->config[INDEX_FILES];
+               struct file file = STRUCT_FILE_INITIALIZER;
+               struct vec filename_vec;
+               size_t n = strlen(path);
+               int found = 0;
+
+               /* The 'path' given to us points to the directory. Remove all trailing
+                * directory separator characters from the end of the path, and
+                * then append single directory separator character. */
+               while (n > 0 && path[n - 1] == '/') {
+                       n--;
+               }
+               path[n] = '/';
+
+               /* Traverse index files list. For each entry, append it to the given
+                * path and see if the file exists. If it exists, break the loop */
+               while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
+                       /* Ignore too long entries that may overflow path buffer */
+                       if (filename_vec.len > path_len - (n + 2)) {
+                               continue;
+                       }
+
+                       /* Prepare full path to the index file */
+                       mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
+
+                       /* Does it exist? */
+                       if (mg_stat(conn, path, &file)) {
+                               /* Yes it does, break the loop */
+                               *filep = file;
+                               found = 1;
+                               break;
+                       }
+               }
+
+               /* If no index file exists, restore directory path */
+               if (!found) {
+                       path[n] = '\0';
+               }
+
+               return found;
+       }
+       return 0;
+}
+#endif
+
+
+#if !defined(NO_CACHING)
+/* Return True if we should reply 304 Not Modified. */
+static int
+is_not_modified(const struct mg_connection *conn, const struct file *filep)
+{
+       char etag[64];
+       const char *ims = mg_get_header(conn, "If-Modified-Since");
+       const char *inm = mg_get_header(conn, "If-None-Match");
+       construct_etag(etag, sizeof(etag), filep);
+       if (!filep) {
+               return 0;
+       }
+       return (inm != NULL && !mg_strcasecmp(etag, inm))
+              || (ims != NULL && (filep->last_modified <= parse_date_string(ims)));
+}
+#endif /* !NO_CACHING */
+
+
+#if !defined(NO_CGI) || !defined(NO_FILES)
+static int
+forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
+{
+       const char *expect, *body;
+       char buf[MG_BUF_LEN];
+       int to_read, nread, success = 0;
+       int64_t buffered_len;
+       double timeout = -1.0;
+
+       if (!conn) {
+               return 0;
+       }
+       if (conn->ctx->config[REQUEST_TIMEOUT]) {
+               timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+       }
+
+       expect = mg_get_header(conn, "Expect");
+       /* assert(fp != NULL); */
+       if (!fp) {
+               send_http_error(conn, 500, "%s", "Error: NULL File");
+               return 0;
+       }
+
+       if (conn->content_len == -1 && !conn->is_chunked) {
+               /* Content length is not specified by the client. */
+               send_http_error(conn,
+                               411,
+                               "%s",
+                               "Error: Client did not specify content length");
+       } else if ((expect != NULL)
+                  && (mg_strcasecmp(expect, "100-continue") != 0)) {
+               /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */
+               send_http_error(conn,
+                               417,
+                               "Error: Can not fulfill expectation %s",
+                               expect);
+       } else {
+               if (expect != NULL) {
+                       (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
+                       conn->status_code = 100;
+               } else {
+                       conn->status_code = 200;
+               }
+
+               buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
+                              - conn->consumed_content;
+
+               /* assert(buffered_len >= 0); */
+               /* assert(conn->consumed_content == 0); */
+
+               if ((buffered_len < 0) || (conn->consumed_content != 0)) {
+                       send_http_error(conn, 500, "%s", "Error: Size mismatch");
+                       return 0;
+               }
+
+               if (buffered_len > 0) {
+                       if ((int64_t)buffered_len > conn->content_len) {
+                               buffered_len = (int)conn->content_len;
+                       }
+                       body = conn->buf + conn->request_len + conn->consumed_content;
+                       push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len);
+                       conn->consumed_content += buffered_len;
+               }
+
+               nread = 0;
+               while (conn->consumed_content < conn->content_len) {
+                       to_read = sizeof(buf);
+                       if ((int64_t)to_read > conn->content_len - conn->consumed_content) {
+                               to_read = (int)(conn->content_len - conn->consumed_content);
+                       }
+                       nread = pull(NULL, conn, buf, to_read, timeout);
+                       if (nread <= 0
+                           || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) {
+                               break;
+                       }
+                       conn->consumed_content += nread;
+               }
+
+               if (conn->consumed_content == conn->content_len) {
+                       success = (nread >= 0);
+               }
+
+               /* Each error code path in this function must send an error */
+               if (!success) {
+                       /* NOTE: Maybe some data has already been sent. */
+                       /* TODO (low): If some data has been sent, a correct error
+                        * reply can no longer be sent, so just close the connection */
+                       send_http_error(conn, 500, "%s", "");
+               }
+       }
+
+       return success;
+}
+#endif
+
+#if !defined(NO_CGI)
+/* This structure helps to create an environment for the spawned CGI program.
+ * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
+ * last element must be NULL.
+ * However, on Windows there is a requirement that all these VARIABLE=VALUE\0
+ * strings must reside in a contiguous buffer. The end of the buffer is
+ * marked by two '\0' characters.
+ * We satisfy both worlds: we create an envp array (which is vars), all
+ * entries are actually pointers inside buf. */
+struct cgi_environment {
+       struct mg_connection *conn;
+       /* Data block */
+       char *buf;      /* Environment buffer */
+       size_t buflen;  /* Space available in buf */
+       size_t bufused; /* Space taken in buf */
+                       /* Index block */
+       char **var;     /* char **envp */
+       size_t varlen;  /* Number of variables available in var */
+       size_t varused; /* Number of variables stored in var */
+};
+
+
+static void addenv(struct cgi_environment *env,
+                   PRINTF_FORMAT_STRING(const char *fmt),
+                   ...) PRINTF_ARGS(2, 3);
+
+/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective
+ * pointer into the vars array. Assumes env != NULL and fmt != NULL. */
+static void
+addenv(struct cgi_environment *env, const char *fmt, ...)
+{
+       size_t n, space;
+       int truncated;
+       char *added;
+       va_list ap;
+
+       /* Calculate how much space is left in the buffer */
+       space = (env->buflen - env->bufused);
+
+       /* Calculate an estimate for the required space */
+       n = strlen(fmt) + 2 + 128;
+
+       do {
+               if (space <= n) {
+                       /* Allocate new buffer */
+                       n = env->buflen + CGI_ENVIRONMENT_SIZE;
+                       added = (char *)mg_realloc(env->buf, n);
+                       if (!added) {
+                               /* Out of memory */
+                               mg_cry(env->conn,
+                                      "%s: Cannot allocate memory for CGI variable [%s]",
+                                      __func__,
+                                      fmt);
+                               return;
+                       }
+                       env->buf = added;
+                       env->buflen = n;
+                       space = (env->buflen - env->bufused);
+               }
+
+               /* Make a pointer to the free space int the buffer */
+               added = env->buf + env->bufused;
+
+               /* Copy VARIABLE=VALUE\0 string into the free space */
+               va_start(ap, fmt);
+               mg_vsnprintf(env->conn, &truncated, added, (size_t)space, fmt, ap);
+               va_end(ap);
+
+               /* Do not add truncated strings to the environment */
+               if (truncated) {
+                       /* Reallocate the buffer */
+                       space = 0;
+                       n = 1;
+               }
+       } while (truncated);
+
+       /* Calculate number of bytes added to the environment */
+       n = strlen(added) + 1;
+       env->bufused += n;
+
+       /* Now update the variable index */
+       space = (env->varlen - env->varused);
+       if (space < 2) {
+               mg_cry(env->conn,
+                      "%s: Cannot register CGI variable [%s]",
+                      __func__,
+                      fmt);
+               return;
+       }
+
+       /* Append a pointer to the added string into the envp array */
+       env->var[env->varused] = added;
+       env->varused++;
+}
+
+
+static void
+prepare_cgi_environment(struct mg_connection *conn,
+                        const char *prog,
+                        struct cgi_environment *env)
+{
+       const char *s;
+       struct vec var_vec;
+       char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
+       int i, truncated;
+
+       if (conn == NULL || prog == NULL || env == NULL) {
+               return;
+       }
+
+       env->conn = conn;
+       env->buflen = CGI_ENVIRONMENT_SIZE;
+       env->bufused = 0;
+       env->buf = (char *)mg_malloc(env->buflen);
+       env->varlen = MAX_CGI_ENVIR_VARS;
+       env->varused = 0;
+       env->var = (char **)mg_malloc(env->buflen * sizeof(char *));
+
+       addenv(env, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
+       addenv(env, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
+       addenv(env, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
+       addenv(env, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version());
+
+       /* Prepare the environment block */
+       addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
+       addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
+       addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
+
+#if defined(USE_IPV6)
+       if (conn->client.lsa.sa.sa_family == AF_INET6) {
+               addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port));
+       } else
+#endif
+       {
+               addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
+       }
+
+       sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+       addenv(env, "REMOTE_ADDR=%s", src_addr);
+
+       addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
+       addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
+
+       addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
+       addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
+
+       /* SCRIPT_NAME */
+       addenv(env,
+              "SCRIPT_NAME=%.*s",
+              (int)strlen(conn->request_info.local_uri)
+                  - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)),
+              conn->request_info.local_uri);
+
+       addenv(env, "SCRIPT_FILENAME=%s", prog);
+       if (conn->path_info == NULL) {
+               addenv(env, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]);
+       } else {
+               addenv(env,
+                      "PATH_TRANSLATED=%s%s",
+                      conn->ctx->config[DOCUMENT_ROOT],
+                      conn->path_info);
+       }
+
+       addenv(env, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
+
+       if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
+               addenv(env, "CONTENT_TYPE=%s", s);
+       }
+       if (conn->request_info.query_string != NULL) {
+               addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
+       }
+       if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
+               addenv(env, "CONTENT_LENGTH=%s", s);
+       }
+       if ((s = getenv("PATH")) != NULL) {
+               addenv(env, "PATH=%s", s);
+       }
+       if (conn->path_info != NULL) {
+               addenv(env, "PATH_INFO=%s", conn->path_info);
+       }
+
+       if (conn->status_code > 0) {
+               /* CGI error handler should show the status code */
+               addenv(env, "STATUS=%d", conn->status_code);
+       }
+
+#if defined(_WIN32)
+       if ((s = getenv("COMSPEC")) != NULL) {
+               addenv(env, "COMSPEC=%s", s);
+       }
+       if ((s = getenv("SYSTEMROOT")) != NULL) {
+               addenv(env, "SYSTEMROOT=%s", s);
+       }
+       if ((s = getenv("SystemDrive")) != NULL) {
+               addenv(env, "SystemDrive=%s", s);
+       }
+       if ((s = getenv("ProgramFiles")) != NULL) {
+               addenv(env, "ProgramFiles=%s", s);
+       }
+       if ((s = getenv("ProgramFiles(x86)")) != NULL) {
+               addenv(env, "ProgramFiles(x86)=%s", s);
+       }
+#else
+       if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
+               addenv(env, "LD_LIBRARY_PATH=%s", s);
+       }
+#endif /* _WIN32 */
+
+       if ((s = getenv("PERLLIB")) != NULL) {
+               addenv(env, "PERLLIB=%s", s);
+       }
+
+       if (conn->request_info.remote_user != NULL) {
+               addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
+               addenv(env, "%s", "AUTH_TYPE=Digest");
+       }
+
+       /* Add all headers as HTTP_* variables */
+       for (i = 0; i < conn->request_info.num_headers; i++) {
+
+               (void)mg_snprintf(conn,
+                                 &truncated,
+                                 http_var_name,
+                                 sizeof(http_var_name),
+                                 "HTTP_%s",
+                                 conn->request_info.http_headers[i].name);
+
+               if (truncated) {
+                       mg_cry(conn,
+                              "%s: HTTP header variable too long [%s]",
+                              __func__,
+                              conn->request_info.http_headers[i].name);
+                       continue;
+               }
+
+               /* Convert variable name into uppercase, and change - to _ */
+               for (p = http_var_name; *p != '\0'; p++) {
+                       if (*p == '-') {
+                               *p = '_';
+                       }
+                       *p = (char)toupper(*(unsigned char *)p);
+               }
+
+               addenv(env,
+                      "%s=%s",
+                      http_var_name,
+                      conn->request_info.http_headers[i].value);
+       }
+
+       /* Add user-specified variables */
+       s = conn->ctx->config[CGI_ENVIRONMENT];
+       while ((s = next_option(s, &var_vec, NULL)) != NULL) {
+               addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
+       }
+
+       env->var[env->varused] = NULL;
+       env->buf[env->bufused] = '\0';
+}
+
+
+static void
+handle_cgi_request(struct mg_connection *conn, const char *prog)
+{
+       char *buf;
+       size_t buflen;
+       int headers_len, data_len, i, truncated;
+       int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
+       const char *status, *status_text, *connection_state;
+       char *pbuf, dir[PATH_MAX], *p;
+       struct mg_request_info ri;
+       struct cgi_environment blk;
+       FILE *in = NULL, *out = NULL, *err = NULL;
+       struct file fout = STRUCT_FILE_INITIALIZER;
+       pid_t pid = (pid_t)-1;
+
+       if (conn == NULL) {
+               return;
+       }
+
+       buf = NULL;
+       buflen = 16384;
+       prepare_cgi_environment(conn, prog, &blk);
+
+       /* CGI must be executed in its own directory. 'dir' must point to the
+        * directory containing executable program, 'p' must point to the
+        * executable program name relative to 'dir'. */
+       (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
+
+       if (truncated) {
+               mg_cry(conn, "Error: CGI program \"%s\": Path too long", prog);
+               send_http_error(conn, 500, "Error: %s", "CGI path too long");
+               goto done;
+       }
+
+       if ((p = strrchr(dir, '/')) != NULL) {
+               *p++ = '\0';
+       } else {
+               dir[0] = '.', dir[1] = '\0';
+               p = (char *)prog;
+       }
+
+       if (pipe(fdin) != 0 || pipe(fdout) != 0 || pipe(fderr) != 0) {
+               status = strerror(ERRNO);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Can not create CGI pipes: %s",
+                      prog,
+                      status);
+               send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", status);
+               goto done;
+       }
+
+       pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir);
+
+       if (pid == (pid_t)-1) {
+               status = strerror(ERRNO);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Can not spawn CGI process: %s",
+                      prog,
+                      status);
+               send_http_error(conn,
+                               500,
+                               "Error: Cannot spawn CGI process [%s]: %s",
+                               prog,
+                               status);
+               goto done;
+       }
+
+       /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */
+       set_close_on_exec((SOCKET)fdin[0], conn);  /* stdin read */
+       set_close_on_exec((SOCKET)fdout[1], conn); /* stdout write */
+       set_close_on_exec((SOCKET)fderr[1], conn); /* stderr write */
+       set_close_on_exec((SOCKET)fdin[1], conn);  /* stdin write */
+       set_close_on_exec((SOCKET)fdout[0], conn); /* stdout read */
+       set_close_on_exec((SOCKET)fderr[0], conn); /* stderr read */
+
+       /* Parent closes only one side of the pipes.
+        * If we don't mark them as closed, close() attempt before
+        * return from this function throws an exception on Windows.
+        * Windows does not like when closed descriptor is closed again. */
+       (void)close(fdin[0]);
+       (void)close(fdout[1]);
+       (void)close(fderr[1]);
+       fdin[0] = fdout[1] = fderr[1] = -1;
+
+       if ((in = fdopen(fdin[1], "wb")) == NULL) {
+               status = strerror(ERRNO);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Can not open stdin: %s",
+                      prog,
+                      status);
+               send_http_error(conn,
+                               500,
+                               "Error: CGI can not open fdin\nfopen: %s",
+                               status);
+               goto done;
+       }
+
+       if ((out = fdopen(fdout[0], "rb")) == NULL) {
+               status = strerror(ERRNO);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Can not open stdout: %s",
+                      prog,
+                      status);
+               send_http_error(conn,
+                               500,
+                               "Error: CGI can not open fdout\nfopen: %s",
+                               status);
+               goto done;
+       }
+
+       if ((err = fdopen(fderr[0], "rb")) == NULL) {
+               status = strerror(ERRNO);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Can not open stderr: %s",
+                      prog,
+                      status);
+               send_http_error(conn,
+                               500,
+                               "Error: CGI can not open fdout\nfopen: %s",
+                               status);
+               goto done;
+       }
+
+       setbuf(in, NULL);
+       setbuf(out, NULL);
+       setbuf(err, NULL);
+       fout.fp = out;
+
+       if ((conn->request_info.content_length > 0) || conn->is_chunked) {
+               /* This is a POST/PUT request, or another request with body data. */
+               if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
+                       /* Error sending the body data */
+                       mg_cry(conn,
+                              "Error: CGI program \"%s\": Forward body data failed",
+                              prog);
+                       goto done;
+               }
+       }
+
+       /* Close so child gets an EOF. */
+       fclose(in);
+       in = NULL;
+       fdin[1] = -1;
+
+       /* Now read CGI reply into a buffer. We need to set correct
+        * status code, thus we need to see all HTTP headers first.
+        * Do not send anything back to client, until we buffer in all
+        * HTTP headers. */
+       data_len = 0;
+       buf = (char *)mg_malloc(buflen);
+       if (buf == NULL) {
+               send_http_error(conn,
+                               500,
+                               "Error: Not enough memory for CGI buffer (%u bytes)",
+                               (unsigned int)buflen);
+               mg_cry(conn,
+                      "Error: CGI program \"%s\": Not enough memory for buffer (%u "
+                      "bytes)",
+                      prog,
+                      (unsigned int)buflen);
+               goto done;
+       }
+       headers_len = read_request(out, conn, buf, (int)buflen, &data_len);
+       if (headers_len <= 0) {
+
+               /* Could not parse the CGI response. Check if some error message on
+                * stderr. */
+               i = pull_all(err, conn, buf, (int)buflen);
+               if (i > 0) {
+                       mg_cry(conn,
+                              "Error: CGI program \"%s\" sent error "
+                              "message: [%.*s]",
+                              prog,
+                              i,
+                              buf);
+                       send_http_error(conn,
+                                       500,
+                                       "Error: CGI program \"%s\" sent error "
+                                       "message: [%.*s]",
+                                       prog,
+                                       i,
+                                       buf);
+               } else {
+                       mg_cry(conn,
+                              "Error: CGI program sent malformed or too big "
+                              "(>%u bytes) HTTP headers: [%.*s]",
+                              (unsigned)buflen,
+                              data_len,
+                              buf);
+
+                       send_http_error(conn,
+                                       500,
+                                       "Error: CGI program sent malformed or too big "
+                                       "(>%u bytes) HTTP headers: [%.*s]",
+                                       (unsigned)buflen,
+                                       data_len,
+                                       buf);
+               }
+
+               goto done;
+       }
+       pbuf = buf;
+       buf[headers_len - 1] = '\0';
+       parse_http_headers(&pbuf, &ri);
+
+       /* Make up and send the status line */
+       status_text = "OK";
+       if ((status = get_header(&ri, "Status")) != NULL) {
+               conn->status_code = atoi(status);
+               status_text = status;
+               while (isdigit(*(const unsigned char *)status_text)
+                      || *status_text == ' ') {
+                       status_text++;
+               }
+       } else if (get_header(&ri, "Location") != NULL) {
+               conn->status_code = 302;
+       } else {
+               conn->status_code = 200;
+       }
+       connection_state = get_header(&ri, "Connection");
+       if (!header_has_option(connection_state, "keep-alive")) {
+               conn->must_close = 1;
+       }
+       (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
+
+       /* Send headers */
+       for (i = 0; i < ri.num_headers; i++) {
+               mg_printf(conn,
+                         "%s: %s\r\n",
+                         ri.http_headers[i].name,
+                         ri.http_headers[i].value);
+       }
+       mg_write(conn, "\r\n", 2);
+
+       /* Send chunk of data that may have been read after the headers */
+       conn->num_bytes_sent +=
+           mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
+
+       /* Read the rest of CGI output and send to the client */
+       send_file_data(conn, &fout, 0, INT64_MAX);
+
+done:
+       mg_free(blk.var);
+       mg_free(blk.buf);
+
+       if (pid != (pid_t)-1) {
+               kill(pid, SIGKILL);
+#if !defined(_WIN32)
+               {
+                       int st;
+                       while (waitpid(pid, &st, 0) != -1)
+                               ; /* clean zombies */
+               }
+#endif
+       }
+       if (fdin[0] != -1) {
+               close(fdin[0]);
+       }
+       if (fdout[1] != -1) {
+               close(fdout[1]);
+       }
+
+       if (in != NULL) {
+               fclose(in);
+       } else if (fdin[1] != -1) {
+               close(fdin[1]);
+       }
+
+       if (out != NULL) {
+               fclose(out);
+       } else if (fdout[0] != -1) {
+               close(fdout[0]);
+       }
+
+       if (err != NULL) {
+               fclose(err);
+       } else if (fderr[0] != -1) {
+               close(fderr[0]);
+       }
+
+       if (buf != NULL) {
+               mg_free(buf);
+       }
+}
+#endif /* !NO_CGI */
+
+
+#if !defined(NO_FILES)
+static void
+mkcol(struct mg_connection *conn, const char *path)
+{
+       int rc, body_len;
+       struct de de;
+       char date[64];
+       time_t curtime = time(NULL);
+
+       if (conn == NULL) {
+               return;
+       }
+
+       /* TODO (mid): Check the send_http_error situations in this function */
+
+       memset(&de.file, 0, sizeof(de.file));
+       if (!mg_stat(conn, path, &de.file)) {
+               mg_cry(conn,
+                      "%s: mg_stat(%s) failed: %s",
+                      __func__,
+                      path,
+                      strerror(ERRNO));
+       }
+
+       if (de.file.last_modified) {
+               /* TODO (high): This check does not seem to make any sense ! */
+               send_http_error(
+                   conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+               return;
+       }
+
+       body_len = conn->data_len - conn->request_len;
+       if (body_len > 0) {
+               send_http_error(
+                   conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+               return;
+       }
+
+       rc = mg_mkdir(conn, path, 0755);
+
+       if (rc == 0) {
+               conn->status_code = 201;
+               gmt_time_string(date, sizeof(date), &curtime);
+               mg_printf(conn,
+                         "HTTP/1.1 %d Created\r\n"
+                         "Date: %s\r\n",
+                         conn->status_code,
+                         date);
+               send_static_cache_header(conn);
+               mg_printf(conn,
+                         "Content-Length: 0\r\n"
+                         "Connection: %s\r\n\r\n",
+                         suggest_connection_header(conn));
+       } else if (rc == -1) {
+               if (errno == EEXIST) {
+                       send_http_error(
+                           conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+               } else if (errno == EACCES) {
+                       send_http_error(
+                           conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+               } else if (errno == ENOENT) {
+                       send_http_error(
+                           conn, 409, "Error: mkcol(%s): %s", path, strerror(ERRNO));
+               } else {
+                       send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO));
+               }
+       }
+}
+
+
+static void
+put_file(struct mg_connection *conn, const char *path)
+{
+       struct file file = STRUCT_FILE_INITIALIZER;
+       const char *range;
+       int64_t r1, r2;
+       int rc;
+       char date[64];
+       time_t curtime = time(NULL);
+
+       if (conn == NULL) {
+               return;
+       }
+
+       if (mg_stat(conn, path, &file)) {
+               /* File already exists */
+               conn->status_code = 200;
+
+               if (file.is_directory) {
+                       /* This is an already existing directory,
+                        * so there is nothing to do for the server. */
+                       rc = 0;
+
+               } else {
+                       /* File exists and is not a directory. */
+                       /* Can it be replaced? */
+
+                       if (file.membuf != NULL) {
+                               /* This is an "in-memory" file, that can not be replaced */
+                               send_http_error(
+                                   conn,
+                                   405,
+                                   "Error: Put not possible\nReplacing %s is not supported",
+                                   path);
+                               return;
+                       }
+
+                       /* Check if the server may write this file */
+                       if (access(path, W_OK) == 0) {
+                               /* Access granted */
+                               conn->status_code = 200;
+                               rc = 1;
+                       } else {
+                               send_http_error(
+                                   conn,
+                                   403,
+                                   "Error: Put not possible\nReplacing %s is not allowed",
+                                   path);
+                               return;
+                       }
+               }
+       } else {
+               /* File should be created */
+               conn->status_code = 201;
+               rc = put_dir(conn, path);
+       }
+
+       if (rc == 0) {
+               /* put_dir returns 0 if path is a directory */
+               gmt_time_string(date, sizeof(date), &curtime);
+               mg_printf(conn,
+                         "HTTP/1.1 %d %s\r\n",
+                         conn->status_code,
+                         mg_get_response_code_text(NULL, conn->status_code));
+               send_no_cache_header(conn);
+               mg_printf(conn,
+                         "Date: %s\r\n"
+                         "Content-Length: 0\r\n"
+                         "Connection: %s\r\n\r\n",
+                         date,
+                         suggest_connection_header(conn));
+
+               /* Request to create a directory has been fulfilled successfully.
+                * No need to put a file. */
+               return;
+       }
+
+       if (rc == -1) {
+               /* put_dir returns -1 if the path is too long */
+               send_http_error(conn,
+                               414,
+                               "Error: Path too long\nput_dir(%s): %s",
+                               path,
+                               strerror(ERRNO));
+               return;
+       }
+
+       if (rc == -2) {
+               /* put_dir returns -2 if the directory can not be created */
+               send_http_error(conn,
+                               500,
+                               "Error: Can not create directory\nput_dir(%s): %s",
+                               path,
+                               strerror(ERRNO));
+               return;
+       }
+
+       /* A file should be created or overwritten. */
+       if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) {
+               mg_fclose(&file);
+               send_http_error(conn,
+                               500,
+                               "Error: Can not create file\nfopen(%s): %s",
+                               path,
+                               strerror(ERRNO));
+               return;
+       }
+
+       fclose_on_exec(&file, conn);
+       range = mg_get_header(conn, "Content-Range");
+       r1 = r2 = 0;
+       if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
+               conn->status_code = 206; /* Partial content */
+               fseeko(file.fp, r1, SEEK_SET);
+       }
+
+       if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) {
+               /* forward_body_data failed.
+                * The error code has already been sent to the client,
+                * and conn->status_code is already set. */
+               mg_fclose(&file);
+               return;
+       }
+
+       gmt_time_string(date, sizeof(date), &curtime);
+       mg_printf(conn,
+                 "HTTP/1.1 %d %s\r\n",
+                 conn->status_code,
+                 mg_get_response_code_text(NULL, conn->status_code));
+       send_no_cache_header(conn);
+       mg_printf(conn,
+                 "Date: %s\r\n"
+                 "Content-Length: 0\r\n"
+                 "Connection: %s\r\n\r\n",
+                 date,
+                 suggest_connection_header(conn));
+
+       mg_fclose(&file);
+}
+
+
+static void
+delete_file(struct mg_connection *conn, const char *path)
+{
+       struct de de;
+       memset(&de.file, 0, sizeof(de.file));
+       if (!mg_stat(conn, path, &de.file)) {
+               /* mg_stat returns 0 if the file does not exist */
+               send_http_error(conn,
+                               404,
+                               "Error: Cannot delete file\nFile %s not found",
+                               path);
+               return;
+       }
+
+       if (de.file.membuf != NULL) {
+               /* the file is cached in memory */
+               send_http_error(
+                   conn,
+                   405,
+                   "Error: Delete not possible\nDeleting %s is not supported",
+                   path);
+               return;
+       }
+
+       if (de.file.is_directory) {
+               if (remove_directory(conn, path)) {
+                       /* Delete is successful: Return 204 without content. */
+                       send_http_error(conn, 204, "%s", "");
+               } else {
+                       /* Delete is not successful: Return 500 (Server error). */
+                       send_http_error(conn, 500, "Error: Could not delete %s", path);
+               }
+               return;
+       }
+
+       /* This is an existing file (not a directory).
+        * Check if write permission is granted. */
+       if (access(path, W_OK) != 0) {
+               /* File is read only */
+               send_http_error(
+                   conn,
+                   403,
+                   "Error: Delete not possible\nDeleting %s is not allowed",
+                   path);
+               return;
+       }
+
+       /* Try to delete it. */
+       if (mg_remove(conn, path) == 0) {
+               /* Delete was successful: Return 204 without content. */
+               send_http_error(conn, 204, "%s", "");
+       } else {
+               /* Delete not successful (file locked). */
+               send_http_error(conn,
+                               423,
+                               "Error: Cannot delete file\nremove(%s): %s",
+                               path,
+                               strerror(ERRNO));
+       }
+}
+#endif /* !NO_FILES */
+
+
+static void
+send_ssi_file(struct mg_connection *, const char *, struct file *, int);
+
+
+static void
+do_ssi_include(struct mg_connection *conn,
+               const char *ssi,
+               char *tag,
+               int include_level)
+{
+       char file_name[MG_BUF_LEN], path[512], *p;
+       struct file file = STRUCT_FILE_INITIALIZER;
+       size_t len;
+       int truncated = 0;
+
+       if (conn == NULL) {
+               return;
+       }
+
+       /* sscanf() is safe here, since send_ssi_file() also uses buffer
+        * of size MG_BUF_LEN to get the tag. So strlen(tag) is
+        * always < MG_BUF_LEN. */
+       if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
+               /* File name is relative to the webserver root */
+               file_name[511] = 0;
+               (void)mg_snprintf(conn,
+                                 &truncated,
+                                 path,
+                                 sizeof(path),
+                                 "%s/%s",
+                                 conn->ctx->config[DOCUMENT_ROOT],
+                                 file_name);
+
+       } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
+               /* File name is relative to the webserver working directory
+                * or it is absolute system path */
+               file_name[511] = 0;
+               (void)
+                   mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
+
+       } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1
+                  || sscanf(tag, " \"%511[^\"]\"", file_name) == 1) {
+               /* File name is relative to the currect document */
+               file_name[511] = 0;
+               (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
+
+               if (!truncated) {
+                       if ((p = strrchr(path, '/')) != NULL) {
+                               p[1] = '\0';
+                       }
+                       len = strlen(path);
+                       (void)mg_snprintf(conn,
+                                         &truncated,
+                                         path + len,
+                                         sizeof(path) - len,
+                                         "%s",
+                                         file_name);
+               }
+
+       } else {
+               mg_cry(conn, "Bad SSI #include: [%s]", tag);
+               return;
+       }
+
+       if (truncated) {
+               mg_cry(conn, "SSI #include path length overflow: [%s]", tag);
+               return;
+       }
+
+       if (!mg_fopen(conn, path, "rb", &file)) {
+               mg_cry(conn,
+                      "Cannot open SSI #include: [%s]: fopen(%s): %s",
+                      tag,
+                      path,
+                      strerror(ERRNO));
+       } else {
+               fclose_on_exec(&file, conn);
+               if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
+                                strlen(conn->ctx->config[SSI_EXTENSIONS]),
+                                path) > 0) {
+                       send_ssi_file(conn, path, &file, include_level + 1);
+               } else {
+                       send_file_data(conn, &file, 0, INT64_MAX);
+               }
+               mg_fclose(&file);
+       }
+}
+
+
+#if !defined(NO_POPEN)
+static void
+do_ssi_exec(struct mg_connection *conn, char *tag)
+{
+       char cmd[1024] = "";
+       struct file file = STRUCT_FILE_INITIALIZER;
+
+       if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
+               mg_cry(conn, "Bad SSI #exec: [%s]", tag);
+       } else {
+               cmd[1023] = 0;
+               if ((file.fp = popen(cmd, "r")) == NULL) {
+                       mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
+               } else {
+                       send_file_data(conn, &file, 0, INT64_MAX);
+                       pclose(file.fp);
+               }
+       }
+}
+#endif /* !NO_POPEN */
+
+
+static int
+mg_fgetc(struct file *filep, int offset)
+{
+       if (filep == NULL) {
+               return EOF;
+       }
+       if (filep->membuf != NULL && offset >= 0
+           && ((unsigned int)(offset)) < filep->size) {
+               return ((const unsigned char *)filep->membuf)[offset];
+       } else if (filep->fp != NULL) {
+               return fgetc(filep->fp);
+       } else {
+               return EOF;
+       }
+}
+
+
+static void
+send_ssi_file(struct mg_connection *conn,
+              const char *path,
+              struct file *filep,
+              int include_level)
+{
+       char buf[MG_BUF_LEN];
+       int ch, offset, len, in_ssi_tag;
+
+       if (include_level > 10) {
+               mg_cry(conn, "SSI #include level is too deep (%s)", path);
+               return;
+       }
+
+       in_ssi_tag = len = offset = 0;
+       while ((ch = mg_fgetc(filep, offset)) != EOF) {
+               if (in_ssi_tag && ch == '>') {
+                       in_ssi_tag = 0;
+                       buf[len++] = (char)ch;
+                       buf[len] = '\0';
+                       /* assert(len <= (int) sizeof(buf)); */
+                       if (len > (int)sizeof(buf)) {
+                               break;
+                       }
+                       if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
+                               /* Not an SSI tag, pass it */
+                               (void)mg_write(conn, buf, (size_t)len);
+                       } else {
+                               if (!memcmp(buf + 5, "include", 7)) {
+                                       do_ssi_include(conn, path, buf + 12, include_level);
+#if !defined(NO_POPEN)
+                               } else if (!memcmp(buf + 5, "exec", 4)) {
+                                       do_ssi_exec(conn, buf + 9);
+#endif /* !NO_POPEN */
+                               } else {
+                                       mg_cry(conn,
+                                              "%s: unknown SSI "
+                                              "command: \"%s\"",
+                                              path,
+                                              buf);
+                               }
+                       }
+                       len = 0;
+               } else if (in_ssi_tag) {
+                       if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
+                               /* Not an SSI tag */
+                               in_ssi_tag = 0;
+                       } else if (len == (int)sizeof(buf) - 2) {
+                               mg_cry(conn, "%s: SSI tag is too large", path);
+                               len = 0;
+                       }
+                       buf[len++] = (char)(ch & 0xff);
+               } else if (ch == '<') {
+                       in_ssi_tag = 1;
+                       if (len > 0) {
+                               mg_write(conn, buf, (size_t)len);
+                       }
+                       len = 0;
+                       buf[len++] = (char)(ch & 0xff);
+               } else {
+                       buf[len++] = (char)(ch & 0xff);
+                       if (len == (int)sizeof(buf)) {
+                               mg_write(conn, buf, (size_t)len);
+                               len = 0;
+                       }
+               }
+       }
+
+       /* Send the rest of buffered data */
+       if (len > 0) {
+               mg_write(conn, buf, (size_t)len);
+       }
+}
+
+
+static void
+handle_ssi_file_request(struct mg_connection *conn,
+                        const char *path,
+                        struct file *filep)
+{
+       char date[64];
+       time_t curtime = time(NULL);
+       const char *cors1, *cors2, *cors3;
+
+       if (conn == NULL || path == NULL || filep == NULL) {
+               return;
+       }
+
+       if (mg_get_header(conn, "Origin")) {
+               /* Cross-origin resource sharing (CORS). */
+               cors1 = "Access-Control-Allow-Origin: ";
+               cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
+               cors3 = "\r\n";
+       } else {
+               cors1 = cors2 = cors3 = "";
+       }
+
+       if (!mg_fopen(conn, path, "rb", filep)) {
+               /* File exists (precondition for calling this function),
+                * but can not be opened by the server. */
+               send_http_error(conn,
+                               500,
+                               "Error: Cannot read file\nfopen(%s): %s",
+                               path,
+                               strerror(ERRNO));
+       } else {
+               conn->must_close = 1;
+               gmt_time_string(date, sizeof(date), &curtime);
+               fclose_on_exec(filep, conn);
+               mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+               send_no_cache_header(conn);
+               mg_printf(conn,
+                         "%s%s%s"
+                         "Date: %s\r\n"
+                         "Content-Type: text/html\r\n"
+                         "Connection: %s\r\n\r\n",
+                         cors1,
+                         cors2,
+                         cors3,
+                         date,
+                         suggest_connection_header(conn));
+               send_ssi_file(conn, path, filep, 0);
+               mg_fclose(filep);
+       }
+}
+
+
+#if !defined(NO_FILES)
+static void
+send_options(struct mg_connection *conn)
+{
+       char date[64];
+       time_t curtime = time(NULL);
+
+       if (!conn) {
+               return;
+       }
+
+       conn->status_code = 200;
+       conn->must_close = 1;
+       gmt_time_string(date, sizeof(date), &curtime);
+
+       mg_printf(conn,
+                 "HTTP/1.1 200 OK\r\n"
+                 "Date: %s\r\n"
+                 /* TODO: "Cache-Control" (?) */
+                 "Connection: %s\r\n"
+                 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, "
+                 "PROPFIND, MKCOL\r\n"
+                 "DAV: 1\r\n\r\n",
+                 date,
+                 suggest_connection_header(conn));
+}
+
+
+/* Writes PROPFIND properties for a collection element */
+static void
+print_props(struct mg_connection *conn, const char *uri, struct file *filep)
+{
+       char mtime[64];
+
+       if (conn == NULL || uri == NULL || filep == NULL) {
+               return;
+       }
+
+       gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
+       conn->num_bytes_sent +=
+           mg_printf(conn,
+                     "<d:response>"
+                     "<d:href>%s</d:href>"
+                     "<d:propstat>"
+                     "<d:prop>"
+                     "<d:resourcetype>%s</d:resourcetype>"
+                     "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
+                     "<d:getlastmodified>%s</d:getlastmodified>"
+                     "</d:prop>"
+                     "<d:status>HTTP/1.1 200 OK</d:status>"
+                     "</d:propstat>"
+                     "</d:response>\n",
+                     uri,
+                     filep->is_directory ? "<d:collection/>" : "",
+                     filep->size,
+                     mtime);
+}
+
+
+static void
+print_dav_dir_entry(struct de *de, void *data)
+{
+       char href[PATH_MAX];
+       char href_encoded[PATH_MAX];
+       int truncated;
+
+       struct mg_connection *conn = (struct mg_connection *)data;
+       if (!de || !conn) {
+               return;
+       }
+       mg_snprintf(conn,
+                   &truncated,
+                   href,
+                   sizeof(href),
+                   "%s%s",
+                   conn->request_info.local_uri,
+                   de->file_name);
+
+       if (!truncated) {
+               mg_url_encode(href, href_encoded, PATH_MAX - 1);
+               print_props(conn, href_encoded, &de->file);
+       }
+}
+
+
+static void
+handle_propfind(struct mg_connection *conn,
+                const char *path,
+                struct file *filep)
+{
+       const char *depth = mg_get_header(conn, "Depth");
+       char date[64];
+       time_t curtime = time(NULL);
+
+       gmt_time_string(date, sizeof(date), &curtime);
+
+       if (!conn || !path || !filep || !conn->ctx) {
+               return;
+       }
+
+       conn->must_close = 1;
+       conn->status_code = 207;
+       mg_printf(conn,
+                 "HTTP/1.1 207 Multi-Status\r\n"
+                 "Date: %s\r\n",
+                 date);
+       send_static_cache_header(conn);
+       mg_printf(conn,
+                 "Connection: %s\r\n"
+                 "Content-Type: text/xml; charset=utf-8\r\n\r\n",
+                 suggest_connection_header(conn));
+
+       conn->num_bytes_sent +=
+           mg_printf(conn,
+                     "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                     "<d:multistatus xmlns:d='DAV:'>\n");
+
+       /* Print properties for the requested resource itself */
+       print_props(conn, conn->request_info.local_uri, filep);
+
+       /* If it is a directory, print directory entries too if Depth is not 0 */
+       if (filep && filep->is_directory
+           && !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")
+           && (depth == NULL || strcmp(depth, "0") != 0)) {
+               scan_directory(conn, path, conn, &print_dav_dir_entry);
+       }
+
+       conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
+}
+#endif
+
+void
+mg_lock_connection(struct mg_connection *conn)
+{
+       if (conn) {
+               (void)pthread_mutex_lock(&conn->mutex);
+       }
+}
+
+void
+mg_unlock_connection(struct mg_connection *conn)
+{
+       if (conn) {
+               (void)pthread_mutex_unlock(&conn->mutex);
+       }
+}
+
+void
+mg_lock_context(struct mg_context *ctx)
+{
+       if (ctx) {
+               (void)pthread_mutex_lock(&ctx->nonce_mutex);
+       }
+}
+
+void
+mg_unlock_context(struct mg_context *ctx)
+{
+       if (ctx) {
+               (void)pthread_mutex_unlock(&ctx->nonce_mutex);
+       }
+}
+
+#if defined(USE_TIMERS)
+#include "timer.inl"
+#endif /* USE_TIMERS */
+
+#ifdef USE_LUA
+#include "mod_lua.inl"
+#endif /* USE_LUA */
+
+#ifdef USE_DUKTAPE
+#include "mod_duktape.inl"
+#endif /* USE_DUKTAPE */
+
+#if defined(USE_WEBSOCKET)
+
+/* START OF SHA-1 code
+ * Copyright(c) By Steve Reid <steve@edmweb.com> */
+#define SHA1HANDSOFF
+
+/* According to current tests (May 2015), the <solarisfixes.h> is not required.
+ *
+ * #if defined(__sun)
+ * #include "solarisfixes.h"
+ * #endif
+ */
+
+
+static int
+is_big_endian(void)
+{
+       static const int n = 1;
+       return ((char *)&n)[0] == 0;
+}
+
+
+union char64long16 {
+       unsigned char c[64];
+       uint32_t l[16];
+};
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+
+static uint32_t
+blk0(union char64long16 *block, int i)
+{
+       /* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */
+       if (!is_big_endian()) {
+               block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00)
+                             | (rol(block->l[i], 8) & 0x00FF00FF);
+       }
+       return block->l[i];
+}
+
+#define blk(i)                                                                 \
+       (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15]   \
+                                   ^ block->l[(i + 2) & 15] ^ block->l[i & 15],   \
+                               1))
+#define R0(v, w, x, y, z, i)                                                   \
+       z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5);        \
+       w = rol(w, 30);
+#define R1(v, w, x, y, z, i)                                                   \
+       z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5);                \
+       w = rol(w, 30);
+#define R2(v, w, x, y, z, i)                                                   \
+       z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5);                        \
+       w = rol(w, 30);
+#define R3(v, w, x, y, z, i)                                                   \
+       z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5);          \
+       w = rol(w, 30);
+#define R4(v, w, x, y, z, i)                                                   \
+       z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5);                        \
+       w = rol(w, 30);
+
+
+typedef struct {
+       uint32_t state[5];
+       uint32_t count[2];
+       unsigned char buffer[64];
+} SHA1_CTX;
+
+
+static void
+SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+       uint32_t a, b, c, d, e;
+       union char64long16 block[1];
+
+       memcpy(block, buffer, 64);
+       a = state[0];
+       b = state[1];
+       c = state[2];
+       d = state[3];
+       e = state[4];
+       R0(a, b, c, d, e, 0);
+       R0(e, a, b, c, d, 1);
+       R0(d, e, a, b, c, 2);
+       R0(c, d, e, a, b, 3);
+       R0(b, c, d, e, a, 4);
+       R0(a, b, c, d, e, 5);
+       R0(e, a, b, c, d, 6);
+       R0(d, e, a, b, c, 7);
+       R0(c, d, e, a, b, 8);
+       R0(b, c, d, e, a, 9);
+       R0(a, b, c, d, e, 10);
+       R0(e, a, b, c, d, 11);
+       R0(d, e, a, b, c, 12);
+       R0(c, d, e, a, b, 13);
+       R0(b, c, d, e, a, 14);
+       R0(a, b, c, d, e, 15);
+       R1(e, a, b, c, d, 16);
+       R1(d, e, a, b, c, 17);
+       R1(c, d, e, a, b, 18);
+       R1(b, c, d, e, a, 19);
+       R2(a, b, c, d, e, 20);
+       R2(e, a, b, c, d, 21);
+       R2(d, e, a, b, c, 22);
+       R2(c, d, e, a, b, 23);
+       R2(b, c, d, e, a, 24);
+       R2(a, b, c, d, e, 25);
+       R2(e, a, b, c, d, 26);
+       R2(d, e, a, b, c, 27);
+       R2(c, d, e, a, b, 28);
+       R2(b, c, d, e, a, 29);
+       R2(a, b, c, d, e, 30);
+       R2(e, a, b, c, d, 31);
+       R2(d, e, a, b, c, 32);
+       R2(c, d, e, a, b, 33);
+       R2(b, c, d, e, a, 34);
+       R2(a, b, c, d, e, 35);
+       R2(e, a, b, c, d, 36);
+       R2(d, e, a, b, c, 37);
+       R2(c, d, e, a, b, 38);
+       R2(b, c, d, e, a, 39);
+       R3(a, b, c, d, e, 40);
+       R3(e, a, b, c, d, 41);
+       R3(d, e, a, b, c, 42);
+       R3(c, d, e, a, b, 43);
+       R3(b, c, d, e, a, 44);
+       R3(a, b, c, d, e, 45);
+       R3(e, a, b, c, d, 46);
+       R3(d, e, a, b, c, 47);
+       R3(c, d, e, a, b, 48);
+       R3(b, c, d, e, a, 49);
+       R3(a, b, c, d, e, 50);
+       R3(e, a, b, c, d, 51);
+       R3(d, e, a, b, c, 52);
+       R3(c, d, e, a, b, 53);
+       R3(b, c, d, e, a, 54);
+       R3(a, b, c, d, e, 55);
+       R3(e, a, b, c, d, 56);
+       R3(d, e, a, b, c, 57);
+       R3(c, d, e, a, b, 58);
+       R3(b, c, d, e, a, 59);
+       R4(a, b, c, d, e, 60);
+       R4(e, a, b, c, d, 61);
+       R4(d, e, a, b, c, 62);
+       R4(c, d, e, a, b, 63);
+       R4(b, c, d, e, a, 64);
+       R4(a, b, c, d, e, 65);
+       R4(e, a, b, c, d, 66);
+       R4(d, e, a, b, c, 67);
+       R4(c, d, e, a, b, 68);
+       R4(b, c, d, e, a, 69);
+       R4(a, b, c, d, e, 70);
+       R4(e, a, b, c, d, 71);
+       R4(d, e, a, b, c, 72);
+       R4(c, d, e, a, b, 73);
+       R4(b, c, d, e, a, 74);
+       R4(a, b, c, d, e, 75);
+       R4(e, a, b, c, d, 76);
+       R4(d, e, a, b, c, 77);
+       R4(c, d, e, a, b, 78);
+       R4(b, c, d, e, a, 79);
+       state[0] += a;
+       state[1] += b;
+       state[2] += c;
+       state[3] += d;
+       state[4] += e;
+       a = b = c = d = e = 0;
+       memset(block, '\0', sizeof(block));
+}
+
+
+static void
+SHA1Init(SHA1_CTX *context)
+{
+       context->state[0] = 0x67452301;
+       context->state[1] = 0xEFCDAB89;
+       context->state[2] = 0x98BADCFE;
+       context->state[3] = 0x10325476;
+       context->state[4] = 0xC3D2E1F0;
+       context->count[0] = context->count[1] = 0;
+}
+
+
+static void
+SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len)
+{
+       uint32_t i, j;
+
+       j = context->count[0];
+       if ((context->count[0] += len << 3) < j) {
+               context->count[1]++;
+       }
+       context->count[1] += (len >> 29);
+       j = (j >> 3) & 63;
+       if ((j + len) > 63) {
+               memcpy(&context->buffer[j], data, (i = 64 - j));
+               SHA1Transform(context->state, context->buffer);
+               for (; i + 63 < len; i += 64) {
+                       SHA1Transform(context->state, &data[i]);
+               }
+               j = 0;
+       } else
+               i = 0;
+       memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+static void
+SHA1Final(unsigned char digest[20], SHA1_CTX *context)
+{
+       unsigned i;
+       unsigned char finalcount[8], c;
+
+       for (i = 0; i < 8; i++) {
+               finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+                                                >> ((3 - (i & 3)) * 8)) & 255);
+       }
+       c = 0200;
+       SHA1Update(context, &c, 1);
+       while ((context->count[0] & 504) != 448) {
+               c = 0000;
+               SHA1Update(context, &c, 1);
+       }
+       SHA1Update(context, finalcount, 8);
+       for (i = 0; i < 20; i++) {
+               digest[i] = (unsigned char)((context->state[i >> 2]
+                                            >> ((3 - (i & 3)) * 8)) & 255);
+       }
+       memset(context, '\0', sizeof(*context));
+       memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* END OF SHA1 CODE */
+
+
+static int
+send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
+{
+       static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+       const char *protocol = NULL;
+       char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
+       SHA1_CTX sha_ctx;
+       int truncated;
+
+       /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */
+       mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic);
+       if (truncated) {
+               conn->must_close = 1;
+               return 0;
+       }
+
+       SHA1Init(&sha_ctx);
+       SHA1Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf));
+       SHA1Final((unsigned char *)sha, &sha_ctx);
+       base64_encode((unsigned char *)sha, sizeof(sha), b64_sha);
+       mg_printf(conn,
+                 "HTTP/1.1 101 Switching Protocols\r\n"
+                 "Upgrade: websocket\r\n"
+                 "Connection: Upgrade\r\n"
+                 "Sec-WebSocket-Accept: %s\r\n",
+                 b64_sha);
+       protocol = mg_get_header(conn, "Sec-WebSocket-Protocol");
+       if (protocol) {
+               /* The protocol is a comma seperated list of names. */
+               /* The server must only return one value from this list. */
+               /* First check if it is a list or just a single value. */
+               const char *sep = strchr(protocol, ',');
+               if (sep == NULL) {
+                       /* Just a single protocol -> accept it. */
+                       mg_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", protocol);
+               } else {
+                       /* Multiple protocols -> accept the first one. */
+                       /* This is just a quick fix if the client offers multiple
+                        * protocols. In order to get the behavior intended by
+                        * RFC 6455 (https://tools.ietf.org/rfc/rfc6455.txt), it is
+                        * required to have a list of websocket subprotocols accepted
+                        * by the server. Then the server must either select a subprotocol
+                        * supported by client and server, or the server has to abort the
+                        * handshake by not returning a Sec-Websocket-Protocol header if
+                        * no subprotocol is acceptable.
+                        */
+                       mg_printf(conn,
+                                 "Sec-WebSocket-Protocol: %.*s\r\n\r\n",
+                                 (int)(sep - protocol),
+                                 protocol);
+               }
+               /* TODO: Real subprotocol negotiation instead of just taking the first
+                * websocket subprotocol suggested by the client. */
+       } else {
+               mg_printf(conn, "%s", "\r\n");
+       }
+
+       return 1;
+}
+
+
+static void
+read_websocket(struct mg_connection *conn,
+               mg_websocket_data_handler ws_data_handler,
+               void *callback_data)
+{
+       /* Pointer to the beginning of the portion of the incoming websocket
+        * message queue.
+        * The original websocket upgrade request is never removed, so the queue
+        * begins after it. */
+       unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
+       int n, error, exit_by_callback;
+
+       /* body_len is the length of the entire queue in bytes
+        * len is the length of the current message
+        * data_len is the length of the current message's data payload
+        * header_len is the length of the current message's header */
+       size_t i, len, mask_len = 0, data_len = 0, header_len, body_len;
+
+       /* "The masking key is a 32-bit value chosen at random by the client."
+        * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
+       */
+       unsigned char mask[4];
+
+       /* data points to the place where the message is stored when passed to
+        * the
+        * websocket_data callback.  This is either mem on the stack, or a
+        * dynamically allocated buffer if it is too large. */
+       char mem[4096];
+       char *data = mem;
+       unsigned char mop; /* mask flag and opcode */
+       double timeout = -1.0;
+
+       if (conn->ctx->config[WEBSOCKET_TIMEOUT]) {
+               timeout = atoi(conn->ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
+       }
+       if ((timeout <= 0.0) && (conn->ctx->config[REQUEST_TIMEOUT])) {
+               timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+       }
+
+       mg_set_thread_name("wsock");
+
+       /* Loop continuously, reading messages from the socket, invoking the
+        * callback, and waiting repeatedly until an error occurs. */
+       while (!conn->ctx->stop_flag) {
+               header_len = 0;
+               assert(conn->data_len >= conn->request_len);
+               if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
+                       len = buf[1] & 127;
+                       mask_len = buf[1] & 128 ? 4 : 0;
+                       if (len < 126 && body_len >= mask_len) {
+                               data_len = len;
+                               header_len = 2 + mask_len;
+                       } else if (len == 126 && body_len >= 4 + mask_len) {
+                               header_len = 4 + mask_len;
+                               data_len = ((((size_t)buf[2]) << 8) + buf[3]);
+                       } else if (body_len >= 10 + mask_len) {
+                               header_len = 10 + mask_len;
+                               data_len = (((uint64_t)ntohl(*(uint32_t *)(void *)&buf[2]))
+                                           << 32) + ntohl(*(uint32_t *)(void *)&buf[6]);
+                       }
+               }
+
+               if (header_len > 0 && body_len >= header_len) {
+                       /* Allocate space to hold websocket payload */
+                       data = mem;
+                       if (data_len > sizeof(mem)) {
+                               data = (char *)mg_malloc(data_len);
+                               if (data == NULL) {
+                                       /* Allocation failed, exit the loop and then close the
+                                        * connection */
+                                       mg_cry(conn, "websocket out of memory; closing connection");
+                                       break;
+                               }
+                       }
+
+                       /* Copy the mask before we shift the queue and destroy it */
+                       if (mask_len > 0) {
+                               memcpy(mask, buf + header_len - mask_len, sizeof(mask));
+                       } else {
+                               memset(mask, 0, sizeof(mask));
+                       }
+
+                       /* Read frame payload from the first message in the queue into
+                        * data and advance the queue by moving the memory in place. */
+                       assert(body_len >= header_len);
+                       if (data_len + header_len > body_len) {
+                               mop = buf[0]; /* current mask and opcode */
+                               /* Overflow case */
+                               len = body_len - header_len;
+                               memcpy(data, buf + header_len, len);
+                               error = 0;
+                               while (len < data_len) {
+                                       n = pull(
+                                           NULL, conn, data + len, (int)(data_len - len), timeout);
+                                       if (n <= 0) {
+                                               error = 1;
+                                               break;
+                                       }
+                                       len += (size_t)n;
+                               }
+                               if (error) {
+                                       mg_cry(conn, "Websocket pull failed; closing connection");
+                                       break;
+                               }
+                               conn->data_len = conn->request_len;
+                       } else {
+                               mop = buf[0]; /* current mask and opcode, overwritten by
+                                              * memmove() */
+                               /* Length of the message being read at the front of the
+                                * queue */
+                               len = data_len + header_len;
+
+                               /* Copy the data payload into the data pointer for the
+                                * callback */
+                               memcpy(data, buf + header_len, data_len);
+
+                               /* Move the queue forward len bytes */
+                               memmove(buf, buf + len, body_len - len);
+
+                               /* Mark the queue as advanced */
+                               conn->data_len -= (int)len;
+                       }
+
+                       /* Apply mask if necessary */
+                       if (mask_len > 0) {
+                               for (i = 0; i < data_len; ++i) {
+                                       data[i] ^= mask[i & 3];
+                               }
+                       }
+
+                       /* Exit the loop if callback signals to exit (server side),
+                        * or "connection close" opcode received (client side). */
+                       exit_by_callback = 0;
+                       if ((ws_data_handler != NULL)
+                           && !ws_data_handler(conn, mop, data, data_len, callback_data)) {
+                               exit_by_callback = 1;
+                       }
+
+                       if (data != mem) {
+                               mg_free(data);
+                       }
+
+                       if (exit_by_callback
+                           || ((mop & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE)) {
+                               /* Opcode == 8, connection close */
+                               break;
+                       }
+
+                       /* Not breaking the loop, process next websocket frame. */
+               } else {
+                       /* Read from the socket into the next available location in the
+                        * message queue. */
+                       if ((n = pull(NULL,
+                                     conn,
+                                     conn->buf + conn->data_len,
+                                     conn->buf_size - conn->data_len,
+                                     timeout)) <= 0) {
+                               /* Error, no bytes read */
+                               break;
+                       }
+                       conn->data_len += n;
+               }
+       }
+
+       mg_set_thread_name("worker");
+}
+
+
+static int
+mg_websocket_write_exec(struct mg_connection *conn,
+                        int opcode,
+                        const char *data,
+                        size_t dataLen,
+                        uint32_t masking_key)
+{
+       unsigned char header[14];
+       size_t headerLen = 1;
+
+       int retval = -1;
+
+       header[0] = 0x80 + (opcode & 0xF);
+
+       /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */
+       if (dataLen < 126) {
+               /* inline 7-bit length field */
+               header[1] = (unsigned char)dataLen;
+               headerLen = 2;
+       } else if (dataLen <= 0xFFFF) {
+               /* 16-bit length field */
+               header[1] = 126;
+               *(uint16_t *)(void *)(header + 2) = htons((uint16_t)dataLen);
+               headerLen = 4;
+       } else {
+               /* 64-bit length field */
+               header[1] = 127;
+               *(uint32_t *)(void *)(header + 2) = htonl((uint64_t)dataLen >> 32);
+               *(uint32_t *)(void *)(header + 6) = htonl(dataLen & 0xFFFFFFFF);
+               headerLen = 10;
+       }
+
+       if (masking_key) {
+               /* add mask */
+               header[1] |= 0x80;
+               *(uint32_t *)(void *)(header + headerLen) = masking_key;
+               headerLen += 4;
+       }
+
+
+       /* Note that POSIX/Winsock's send() is threadsafe
+        * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
+        * but mongoose's mg_printf/mg_write is not (because of the loop in
+        * push(), although that is only a problem if the packet is large or
+        * outgoing buffer is full). */
+       (void)mg_lock_connection(conn);
+       retval = mg_write(conn, header, headerLen);
+       if (dataLen > 0) {
+               retval = mg_write(conn, data, dataLen);
+       }
+       mg_unlock_connection(conn);
+
+       return retval;
+}
+
+int
+mg_websocket_write(struct mg_connection *conn,
+                   int opcode,
+                   const char *data,
+                   size_t dataLen)
+{
+       return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
+}
+
+
+static void
+mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out)
+{
+       size_t i = 0;
+
+       i = 0;
+       if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
+               /* Convert in 32 bit words, if data is 4 byte aligned */
+               while (i < (in_len - 3)) {
+                       *(uint32_t *)(void *)(out + i) =
+                           *(uint32_t *)(void *)(in + i) ^ masking_key;
+                       i += 4;
+               }
+       }
+       if (i != in_len) {
+               /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/
+               while (i < in_len) {
+                       *(uint8_t *)(void *)(out + i) =
+                           *(uint8_t *)(void *)(in + i)
+                           ^ *(((uint8_t *)&masking_key) + (i % 4));
+                       i++;
+               }
+       }
+}
+
+
+int
+mg_websocket_client_write(struct mg_connection *conn,
+                          int opcode,
+                          const char *data,
+                          size_t dataLen)
+{
+       int retval = -1;
+       char *masked_data = (char *)mg_malloc(((dataLen + 7) / 4) * 4);
+       uint32_t masking_key = (uint32_t)get_random();
+
+       if (masked_data == NULL) {
+               /* Return -1 in an error case */
+               mg_cry(conn,
+                      "Cannot allocate buffer for masked websocket response: "
+                      "Out of memory");
+               return -1;
+       }
+
+       mask_data(data, dataLen, masking_key, masked_data);
+
+       retval = mg_websocket_write_exec(
+           conn, opcode, masked_data, dataLen, masking_key);
+       mg_free(masked_data);
+
+       return retval;
+}
+
+
+static void
+handle_websocket_request(struct mg_connection *conn,
+                         const char *path,
+                         int is_callback_resource,
+                         mg_websocket_connect_handler ws_connect_handler,
+                         mg_websocket_ready_handler ws_ready_handler,
+                         mg_websocket_data_handler ws_data_handler,
+                         mg_websocket_close_handler ws_close_handler,
+                         void *cbData)
+{
+       const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key");
+       const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
+       int lua_websock = 0;
+
+#if !defined(USE_LUA)
+       (void)path;
+#endif
+
+       /* Step 1: Check websocket protocol version. */
+       /* Step 1.1: Check Sec-WebSocket-Key. */
+       if (!websock_key) {
+               /* The RFC standard version (https://tools.ietf.org/html/rfc6455)
+                * requires a Sec-WebSocket-Key header.
+                */
+               /* It could be the hixie draft version
+                * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76).
+                */
+               const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1");
+               const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2");
+               char key3[8];
+
+               if ((key1 != NULL) && (key2 != NULL)) {
+                       /* This version uses 8 byte body data in a GET request */
+                       conn->content_len = 8;
+                       if (8 == mg_read(conn, key3, 8)) {
+                               /* This is the hixie version */
+                               send_http_error(conn,
+                                               426,
+                                               "%s",
+                                               "Protocol upgrade to RFC 6455 required");
+                               return;
+                       }
+               }
+               /* This is an unknown version */
+               send_http_error(conn, 400, "%s", "Malformed websocket request");
+               return;
+       }
+
+       /* Step 1.2: Check websocket protocol version. */
+       /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */
+       if (version == NULL || strcmp(version, "13") != 0) {
+               /* Reject wrong versions */
+               send_http_error(conn, 426, "%s", "Protocol upgrade required");
+               return;
+       }
+
+       /* Step 1.3: Could check for "Host", but we do not really nead this
+        * value for anything, so just ignore it. */
+
+       /* Step 2: If a callback is responsible, call it. */
+       if (is_callback_resource) {
+               if (ws_connect_handler != NULL
+                   && ws_connect_handler(conn, cbData) != 0) {
+                       /* C callback has returned non-zero, do not proceed with
+                        * handshake.
+                        */
+                       /* Note that C callbacks are no longer called when Lua is
+                        * responsible, so C can no longer filter callbacks for Lua. */
+                       return;
+               }
+       }
+#if defined(USE_LUA)
+       /* Step 3: No callback. Check if Lua is responsible. */
+       else {
+               /* Step 3.1: Check if Lua is responsible. */
+               if (conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
+                       lua_websock =
+                           match_prefix(conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS],
+                                        strlen(
+                                            conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]),
+                                        path);
+               }
+
+               if (lua_websock) {
+                       /* Step 3.2: Lua is responsible: call it. */
+                       conn->lua_websocket_state = lua_websocket_new(path, conn);
+                       if (!conn->lua_websocket_state) {
+                               /* Lua rejected the new client */
+                               return;
+                       }
+               }
+       }
+#endif
+
+       /* Step 4: Check if there is a responsible websocket handler. */
+       if (!is_callback_resource && !lua_websock) {
+               /* There is no callback, an Lua is not responsible either. */
+               /* Reply with a 404 Not Found or with nothing at all?
+                * TODO (mid): check the websocket standards, how to reply to
+                * requests to invalid websocket addresses. */
+               send_http_error(conn, 404, "%s", "Not found");
+               return;
+       }
+
+       /* Step 5: The websocket connection has been accepted */
+       if (!send_websocket_handshake(conn, websock_key)) {
+               send_http_error(conn, 500, "%s", "Websocket handshake failed");
+               return;
+       }
+
+       /* Step 6: Call the ready handler */
+       if (is_callback_resource) {
+               if (ws_ready_handler != NULL) {
+                       ws_ready_handler(conn, cbData);
+               }
+#if defined(USE_LUA)
+       } else if (lua_websock) {
+               if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
+                       /* the ready handler returned false */
+                       return;
+               }
+#endif
+       }
+
+       /* Step 7: Enter the read loop */
+       if (is_callback_resource) {
+               read_websocket(conn, ws_data_handler, cbData);
+#if defined(USE_LUA)
+       } else if (lua_websock) {
+               read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
+#endif
+       }
+
+       /* Step 8: Call the close handler */
+       if (ws_close_handler) {
+               ws_close_handler(conn, cbData);
+       }
+}
+
+
+static int
+is_websocket_protocol(const struct mg_connection *conn)
+{
+       const char *upgrade, *connection;
+
+       /* A websocket protocoll has the following HTTP headers:
+        *
+        * Connection: Upgrade
+        * Upgrade: Websocket
+        */
+
+       upgrade = mg_get_header(conn, "Upgrade");
+       if (upgrade == NULL) {
+               return 0; /* fail early, don't waste time checking other header
+                          * fields
+                            */
+       }
+       if (!mg_strcasestr(upgrade, "websocket")) {
+               return 0;
+       }
+
+       connection = mg_get_header(conn, "Connection");
+       if (connection == NULL) {
+               return 0;
+       }
+       if (!mg_strcasestr(connection, "upgrade")) {
+               return 0;
+       }
+
+       /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
+        * "Sec-WebSocket-Version" are also required.
+        * Don't check them here, since even an unsupported websocket protocol
+        * request still IS a websocket request (in contrast to a standard HTTP
+        * request). It will fail later in handle_websocket_request.
+        */
+
+       return 1;
+}
+#endif /* !USE_WEBSOCKET */
+
+
+static int
+isbyte(int n)
+{
+       return n >= 0 && n <= 255;
+}
+
+
+static int
+parse_net(const char *spec, uint32_t *net, uint32_t *mask)
+{
+       int n, a, b, c, d, slash = 32, len = 0;
+
+       if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5
+            || sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && isbyte(a)
+           && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0
+           && slash < 33) {
+               len = n;
+               *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8)
+                      | (uint32_t)d;
+               *mask = slash ? 0xffffffffU << (32 - slash) : 0;
+       }
+
+       return len;
+}
+
+
+static int
+set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
+{
+       int throttle = 0;
+       struct vec vec, val;
+       uint32_t net, mask;
+       char mult;
+       double v;
+
+       while ((spec = next_option(spec, &vec, &val)) != NULL) {
+               mult = ',';
+               if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0
+                   || (lowercase(&mult) != 'k' && lowercase(&mult) != 'm'
+                       && mult != ',')) {
+                       continue;
+               }
+               v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576
+                                                                             : 1;
+               if (vec.len == 1 && vec.ptr[0] == '*') {
+                       throttle = (int)v;
+               } else if (parse_net(vec.ptr, &net, &mask) > 0) {
+                       if ((remote_ip & mask) == net) {
+                               throttle = (int)v;
+                       }
+               } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
+                       throttle = (int)v;
+               }
+       }
+
+       return throttle;
+}
+
+
+static uint32_t
+get_remote_ip(const struct mg_connection *conn)
+{
+       if (!conn) {
+               return 0;
+       }
+       return ntohl(*(const uint32_t *)&conn->client.rsa.sin.sin_addr);
+}
+
+
+/* The mg_upload function is superseeded by mg_handle_form_request. */
+#include "handle_form.inl"
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* Implement the deprecated mg_upload function by calling the new
+ * mg_handle_form_request function. While mg_upload could only handle
+ * HTML forms sent as POST request in multipart/form-data format
+ * containing only file input elements, mg_handle_form_request can
+ * handle all form input elements and all standard request methods. */
+struct mg_upload_user_data {
+       struct mg_connection *conn;
+       const char *destination_dir;
+       int num_uploaded_files;
+};
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_found(const char *key,
+                      const char *filename,
+                      char *path,
+                      size_t pathlen,
+                      void *user_data)
+{
+       int truncated = 0;
+       struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
+       (void)key;
+
+       if (!filename) {
+               mg_cry(fud->conn, "%s: No filename set", __func__);
+               return FORM_FIELD_STORAGE_ABORT;
+       }
+       mg_snprintf(fud->conn,
+                   &truncated,
+                   path,
+                   pathlen - 1,
+                   "%s/%s",
+                   fud->destination_dir,
+                   filename);
+       if (!truncated) {
+               mg_cry(fud->conn, "%s: File path too long", __func__);
+               return FORM_FIELD_STORAGE_ABORT;
+       }
+       return FORM_FIELD_STORAGE_STORE;
+}
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_get(const char *key,
+                    const char *value,
+                    size_t value_size,
+                    void *user_data)
+{
+       /* Function should never be called */
+       (void)key;
+       (void)value;
+       (void)value_size;
+       (void)user_data;
+
+       return 0;
+}
+
+
+/* Helper function for deprecated mg_upload. */
+static int
+mg_upload_field_stored(const char *path, long long file_size, void *user_data)
+{
+       struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
+       (void)file_size;
+
+       fud->num_uploaded_files++;
+       fud->conn->ctx->callbacks.upload(fud->conn, path);
+
+       return 0;
+}
+
+
+/* Deprecated function mg_upload - use mg_handle_form_request instead. */
+int
+mg_upload(struct mg_connection *conn, const char *destination_dir)
+{
+       struct mg_upload_user_data fud = {conn, destination_dir, 0};
+       struct mg_form_data_handler fdh = {mg_upload_field_found,
+                                          mg_upload_field_get,
+                                          mg_upload_field_stored,
+                                          0};
+       int ret;
+
+       fdh.user_data = (void *)&fud;
+       ret = mg_handle_form_request(conn, &fdh);
+
+       if (ret < 0) {
+               mg_cry(conn, "%s: Error while parsing the request", __func__);
+       }
+
+       return fud.num_uploaded_files;
+}
+#endif
+
+
+static int
+get_first_ssl_listener_index(const struct mg_context *ctx)
+{
+       unsigned int i;
+       int idx = -1;
+       if (ctx) {
+               for (i = 0; idx == -1 && i < ctx->num_listening_sockets; i++) {
+                       idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
+               }
+       }
+       return idx;
+}
+
+
+static void
+redirect_to_https_port(struct mg_connection *conn, int ssl_index)
+{
+       char host[1025];
+       const char *host_header;
+       size_t hostlen;
+
+       host_header = mg_get_header(conn, "Host");
+       hostlen = sizeof(host);
+       if (host_header != NULL) {
+               char *pos;
+
+               mg_strlcpy(host, host_header, hostlen);
+               host[hostlen - 1] = '\0';
+               pos = strchr(host, ':');
+               if (pos != NULL) {
+                       *pos = '\0';
+               }
+       } else {
+               /* Cannot get host from the Host: header.
+                * Fallback to our IP address. */
+               if (conn) {
+                       sockaddr_to_string(host, hostlen, &conn->client.lsa);
+               }
+       }
+
+       /* Send host, port, uri and (if it exists) ?query_string */
+       if (conn) {
+               mg_printf(conn,
+                         "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n",
+                         host,
+                         (int)ntohs(
+                             conn->ctx->listening_sockets[ssl_index].lsa.sin.sin_port),
+                         conn->request_info.local_uri,
+                         (conn->request_info.query_string == NULL) ? "" : "?",
+                         (conn->request_info.query_string == NULL)
+                             ? ""
+                             : conn->request_info.query_string);
+       }
+}
+
+
+static void
+mg_set_handler_type(struct mg_context *ctx,
+                    const char *uri,
+                    int handler_type,
+                    int is_delete_request,
+                    mg_request_handler handler,
+                    mg_websocket_connect_handler connect_handler,
+                    mg_websocket_ready_handler ready_handler,
+                    mg_websocket_data_handler data_handler,
+                    mg_websocket_close_handler close_handler,
+                    mg_authorization_handler auth_handler,
+                    void *cbdata)
+{
+       struct mg_handler_info *tmp_rh, **lastref;
+       size_t urilen = strlen(uri);
+
+       if (handler_type == WEBSOCKET_HANDLER) {
+               /* assert(handler == NULL); */
+               /* assert(is_delete_request || connect_handler!=NULL ||
+                *        ready_handler!=NULL || data_handler!=NULL ||
+                *        close_handler!=NULL);
+                */
+               /* assert(auth_handler == NULL); */
+               if (handler != NULL) {
+                       return;
+               }
+               if (!is_delete_request && connect_handler == NULL
+                   && ready_handler == NULL
+                   && data_handler == NULL
+                   && close_handler == NULL) {
+                       return;
+               }
+               if (auth_handler != NULL) {
+                       return;
+               }
+       } else if (handler_type == REQUEST_HANDLER) {
+               /* assert(connect_handler==NULL && ready_handler==NULL &&
+                *        data_handler==NULL && close_handler==NULL); */
+               /* assert(is_delete_request || (handler!=NULL));
+                */
+               /* assert(auth_handler == NULL); */
+               if (connect_handler != NULL || ready_handler != NULL
+                   || data_handler != NULL
+                   || close_handler != NULL) {
+                       return;
+               }
+               if (!is_delete_request && (handler == NULL)) {
+                       return;
+               }
+               if (auth_handler != NULL) {
+                       return;
+               }
+       } else { /* AUTH_HANDLER */
+                    /* assert(handler == NULL); */
+                    /* assert(connect_handler==NULL && ready_handler==NULL &&
+                     *        data_handler==NULL && close_handler==NULL); */
+               /* assert(auth_handler != NULL); */
+               if (handler != NULL) {
+                       return;
+               }
+               if (connect_handler != NULL || ready_handler != NULL
+                   || data_handler != NULL
+                   || close_handler != NULL) {
+                       return;
+               }
+               if (!is_delete_request && (auth_handler == NULL)) {
+                       return;
+               }
+       }
+
+       if (!ctx) {
+               return;
+       }
+
+       mg_lock_context(ctx);
+
+       /* first try to find an existing handler */
+       lastref = &(ctx->handlers);
+       for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
+               if (tmp_rh->handler_type == handler_type) {
+                       if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
+                               if (!is_delete_request) {
+                                       /* update existing handler */
+                                       if (handler_type == REQUEST_HANDLER) {
+                                               tmp_rh->handler = handler;
+                                       } else if (handler_type == WEBSOCKET_HANDLER) {
+                                               tmp_rh->connect_handler = connect_handler;
+                                               tmp_rh->ready_handler = ready_handler;
+                                               tmp_rh->data_handler = data_handler;
+                                               tmp_rh->close_handler = close_handler;
+                                       } else { /* AUTH_HANDLER */
+                                               tmp_rh->auth_handler = auth_handler;
+                                       }
+                                       tmp_rh->cbdata = cbdata;
+                               } else {
+                                       /* remove existing handler */
+                                       *lastref = tmp_rh->next;
+                                       mg_free(tmp_rh->uri);
+                                       mg_free(tmp_rh);
+                               }
+                               mg_unlock_context(ctx);
+                               return;
+                       }
+               }
+               lastref = &(tmp_rh->next);
+       }
+
+       if (is_delete_request) {
+               /* no handler to set, this was a remove request to a non-existing
+                * handler */
+               mg_unlock_context(ctx);
+               return;
+       }
+
+       tmp_rh =
+           (struct mg_handler_info *)mg_calloc(sizeof(struct mg_handler_info), 1);
+       if (tmp_rh == NULL) {
+               mg_unlock_context(ctx);
+               mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM");
+               return;
+       }
+       tmp_rh->uri = mg_strdup(uri);
+       if (!tmp_rh->uri) {
+               mg_unlock_context(ctx);
+               mg_free(tmp_rh);
+               mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM");
+               return;
+       }
+       tmp_rh->uri_len = urilen;
+       if (handler_type == REQUEST_HANDLER) {
+               tmp_rh->handler = handler;
+       } else if (handler_type == WEBSOCKET_HANDLER) {
+               tmp_rh->connect_handler = connect_handler;
+               tmp_rh->ready_handler = ready_handler;
+               tmp_rh->data_handler = data_handler;
+               tmp_rh->close_handler = close_handler;
+       } else { /* AUTH_HANDLER */
+               tmp_rh->auth_handler = auth_handler;
+       }
+       tmp_rh->cbdata = cbdata;
+       tmp_rh->handler_type = handler_type;
+       tmp_rh->next = NULL;
+
+       *lastref = tmp_rh;
+       mg_unlock_context(ctx);
+}
+
+
+void
+mg_set_request_handler(struct mg_context *ctx,
+                       const char *uri,
+                       mg_request_handler handler,
+                       void *cbdata)
+{
+       mg_set_handler_type(ctx,
+                           uri,
+                           REQUEST_HANDLER,
+                           handler == NULL,
+                           handler,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           cbdata);
+}
+
+
+void
+mg_set_websocket_handler(struct mg_context *ctx,
+                         const char *uri,
+                         mg_websocket_connect_handler connect_handler,
+                         mg_websocket_ready_handler ready_handler,
+                         mg_websocket_data_handler data_handler,
+                         mg_websocket_close_handler close_handler,
+                         void *cbdata)
+{
+       int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
+                               && (data_handler == NULL)
+                               && (close_handler == NULL);
+       mg_set_handler_type(ctx,
+                           uri,
+                           WEBSOCKET_HANDLER,
+                           is_delete_request,
+                           NULL,
+                           connect_handler,
+                           ready_handler,
+                           data_handler,
+                           close_handler,
+                           NULL,
+                           cbdata);
+}
+
+
+void
+mg_set_auth_handler(struct mg_context *ctx,
+                    const char *uri,
+                    mg_request_handler handler,
+                    void *cbdata)
+{
+       mg_set_handler_type(ctx,
+                           uri,
+                           AUTH_HANDLER,
+                           handler == NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           handler,
+                           cbdata);
+}
+
+
+static int
+get_request_handler(struct mg_connection *conn,
+                    int handler_type,
+                    mg_request_handler *handler,
+                    mg_websocket_connect_handler *connect_handler,
+                    mg_websocket_ready_handler *ready_handler,
+                    mg_websocket_data_handler *data_handler,
+                    mg_websocket_close_handler *close_handler,
+                    mg_authorization_handler *auth_handler,
+                    void **cbdata)
+{
+       const struct mg_request_info *request_info = mg_get_request_info(conn);
+       if (request_info) {
+               const char *uri = request_info->local_uri;
+               size_t urilen = strlen(uri);
+               struct mg_handler_info *tmp_rh;
+
+               if (!conn || !conn->ctx) {
+                       return 0;
+               }
+
+               mg_lock_context(conn->ctx);
+
+               /* first try for an exact match */
+               for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+                    tmp_rh = tmp_rh->next) {
+                       if (tmp_rh->handler_type == handler_type) {
+                               if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
+                                       if (handler_type == WEBSOCKET_HANDLER) {
+                                               *connect_handler = tmp_rh->connect_handler;
+                                               *ready_handler = tmp_rh->ready_handler;
+                                               *data_handler = tmp_rh->data_handler;
+                                               *close_handler = tmp_rh->close_handler;
+                                       } else if (handler_type == REQUEST_HANDLER) {
+                                               *handler = tmp_rh->handler;
+                                       } else { /* AUTH_HANDLER */
+                                               *auth_handler = tmp_rh->auth_handler;
+                                       }
+                                       *cbdata = tmp_rh->cbdata;
+                                       mg_unlock_context(conn->ctx);
+                                       return 1;
+                               }
+                       }
+               }
+
+               /* next try for a partial match, we will accept uri/something */
+               for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+                    tmp_rh = tmp_rh->next) {
+                       if (tmp_rh->handler_type == handler_type) {
+                               if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/'
+                                   && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
+                                       if (handler_type == WEBSOCKET_HANDLER) {
+                                               *connect_handler = tmp_rh->connect_handler;
+                                               *ready_handler = tmp_rh->ready_handler;
+                                               *data_handler = tmp_rh->data_handler;
+                                               *close_handler = tmp_rh->close_handler;
+                                       } else if (handler_type == REQUEST_HANDLER) {
+                                               *handler = tmp_rh->handler;
+                                       } else { /* AUTH_HANDLER */
+                                               *auth_handler = tmp_rh->auth_handler;
+                                       }
+                                       *cbdata = tmp_rh->cbdata;
+                                       mg_unlock_context(conn->ctx);
+                                       return 1;
+                               }
+                       }
+               }
+
+               /* finally try for pattern match */
+               for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
+                    tmp_rh = tmp_rh->next) {
+                       if (tmp_rh->handler_type == handler_type) {
+                               if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
+                                       if (handler_type == WEBSOCKET_HANDLER) {
+                                               *connect_handler = tmp_rh->connect_handler;
+                                               *ready_handler = tmp_rh->ready_handler;
+                                               *data_handler = tmp_rh->data_handler;
+                                               *close_handler = tmp_rh->close_handler;
+                                       } else if (handler_type == REQUEST_HANDLER) {
+                                               *handler = tmp_rh->handler;
+                                       } else { /* AUTH_HANDLER */
+                                               *auth_handler = tmp_rh->auth_handler;
+                                       }
+                                       *cbdata = tmp_rh->cbdata;
+                                       mg_unlock_context(conn->ctx);
+                                       return 1;
+                               }
+                       }
+               }
+
+               mg_unlock_context(conn->ctx);
+       }
+       return 0; /* none found */
+}
+
+
+#if defined(USE_WEBSOCKET) && defined(MG_LEGACY_INTERFACE)
+static int
+deprecated_websocket_connect_wrapper(const struct mg_connection *conn,
+                                     void *cbdata)
+{
+       struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+       if (pcallbacks->websocket_connect) {
+               return pcallbacks->websocket_connect(conn);
+       }
+       /* No handler set - assume "OK" */
+       return 0;
+}
+
+
+static void
+deprecated_websocket_ready_wrapper(struct mg_connection *conn, void *cbdata)
+{
+       struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+       if (pcallbacks->websocket_ready) {
+               pcallbacks->websocket_ready(conn);
+       }
+}
+
+
+static int
+deprecated_websocket_data_wrapper(struct mg_connection *conn,
+                                  int bits,
+                                  char *data,
+                                  size_t len,
+                                  void *cbdata)
+{
+       struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
+       if (pcallbacks->websocket_data) {
+               return pcallbacks->websocket_data(conn, bits, data, len);
+       }
+       /* No handler set - assume "OK" */
+       return 1;
+}
+#endif
+
+
+/* This is the heart of the Civetweb's logic.
+ * This function is called when the request is read, parsed and validated,
+ * and Civetweb must decide what action to take: serve a file, or
+ * a directory, or call embedded function, etcetera. */
+static void
+handle_request(struct mg_connection *conn)
+{
+       if (conn) {
+               struct mg_request_info *ri = &conn->request_info;
+               char path[PATH_MAX];
+               int uri_len, ssl_index;
+               int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
+                   is_put_or_delete_request = 0, is_callback_resource = 0;
+               int i;
+               struct file file = STRUCT_FILE_INITIALIZER;
+               mg_request_handler callback_handler = NULL;
+               mg_websocket_connect_handler ws_connect_handler = NULL;
+               mg_websocket_ready_handler ws_ready_handler = NULL;
+               mg_websocket_data_handler ws_data_handler = NULL;
+               mg_websocket_close_handler ws_close_handler = NULL;
+               void *callback_data = NULL;
+               mg_authorization_handler auth_handler = NULL;
+               void *auth_callback_data = NULL;
+#if !defined(NO_FILES)
+               time_t curtime = time(NULL);
+               char date[64];
+#endif
+
+               path[0] = 0;
+
+               if (!ri) {
+                       return;
+               }
+
+               /* 1. get the request url */
+               /* 1.1. split into url and query string */
+               if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
+                   != NULL) {
+                       *((char *)conn->request_info.query_string++) = '\0';
+               }
+               uri_len = (int)strlen(ri->local_uri);
+
+               /* 1.2. decode url (if config says so) */
+               if (should_decode_url(conn)) {
+                       mg_url_decode(
+                           ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0);
+               }
+
+               /* 1.3. clean URIs, so a path like allowed_dir/../forbidden_file is
+                * not possible */
+               remove_double_dots_and_double_slashes((char *)ri->local_uri);
+
+               /* step 1. completed, the url is known now */
+               DEBUG_TRACE("URL: %s", ri->local_uri);
+
+               /* 2. do a https redirect, if required */
+               if (!conn->client.is_ssl && conn->client.ssl_redir) {
+                       ssl_index = get_first_ssl_listener_index(conn->ctx);
+                       if (ssl_index >= 0) {
+                               redirect_to_https_port(conn, ssl_index);
+                       } else {
+                               /* A http to https forward port has been specified,
+                                * but no https port to forward to. */
+                               send_http_error(conn,
+                                               503,
+                                               "%s",
+                                               "Error: SSL forward not configured properly");
+                               mg_cry(conn, "Can not redirect to SSL, no SSL port available");
+                       }
+                       return;
+               }
+
+               /* 3. if this ip has limited speed, set it for this connection */
+               conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
+                                             get_remote_ip(conn),
+                                             ri->local_uri);
+
+               /* 4. call a "handle everything" callback, if registered */
+               if (conn->ctx->callbacks.begin_request != NULL) {
+                       /* Note that since V1.7 the "begin_request" function is called
+                        * before an authorization check. If an authorization check is
+                        * required, use a request_handler instead. */
+                       i = conn->ctx->callbacks.begin_request(conn);
+                       if (i > 0) {
+                               /* callback already processed the request. Store the
+                                  return value as a status code for the access log. */
+                               conn->status_code = i;
+                               return;
+                       } else if (i == 0) {
+                               /* civetweb should process the request */
+                       } else {
+                               /* unspecified - may change with the next version */
+                               return;
+                       }
+               }
+
+               /* request not yet handled by a handler or redirect, so the request
+                * is processed here */
+
+               /* 5. interpret the url to find out how the request must be handled
+                */
+               /* 5.1. first test, if the request targets the regular http(s)://
+                * protocol namespace or the websocket ws(s):// protocol namespace.
+                */
+               is_websocket_request = is_websocket_protocol(conn);
+
+               /* 5.2. check if the request will be handled by a callback */
+               if (get_request_handler(conn,
+                                       is_websocket_request ? WEBSOCKET_HANDLER
+                                                            : REQUEST_HANDLER,
+                                       &callback_handler,
+                                       &ws_connect_handler,
+                                       &ws_ready_handler,
+                                       &ws_data_handler,
+                                       &ws_close_handler,
+                                       NULL,
+                                       &callback_data)) {
+                       /* 5.2.1. A callback will handle this request. All requests
+                        * handled
+                        * by a callback have to be considered as requests to a script
+                        * resource. */
+                       is_callback_resource = 1;
+                       is_script_resource = 1;
+                       is_put_or_delete_request = is_put_or_delete_method(conn);
+               } else {
+               no_callback_resource:
+                       /* 5.2.2. No callback is responsible for this request. The URI
+                        * addresses a file based resource (static content or Lua/cgi
+                        * scripts in the file system). */
+                       is_callback_resource = 0;
+                       interpret_uri(conn,
+                                     path,
+                                     sizeof(path),
+                                     &file,
+                                     &is_found,
+                                     &is_script_resource,
+                                     &is_websocket_request,
+                                     &is_put_or_delete_request);
+               }
+
+               /* 6. authorization check */
+               /* 6.1. a custom authorization handler is installed */
+               if (get_request_handler(conn,
+                                       AUTH_HANDLER,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &auth_handler,
+                                       &auth_callback_data)) {
+                       if (!auth_handler(conn, auth_callback_data)) {
+                               return;
+                       }
+               } else if (is_put_or_delete_request && !is_script_resource
+                          && !is_callback_resource) {
+/* 6.2. this request is a PUT/DELETE to a real file */
+/* 6.2.1. thus, the server must have real files */
+#if defined(NO_FILES)
+                       if (1) {
+#else
+                       if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
+#endif
+                               /* This server does not have any real files, thus the
+                                * PUT/DELETE methods are not valid. */
+                               send_http_error(conn,
+                                               405,
+                                               "%s method not allowed",
+                                               conn->request_info.request_method);
+                               return;
+                       }
+
+#if !defined(NO_FILES)
+                       /* 6.2.2. Check if put authorization for static files is
+                        * available.
+                        */
+                       if (!is_authorized_for_put(conn)) {
+                               send_authorization_request(conn);
+                               return;
+                       }
+#endif
+
+               } else {
+                       /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
+                        * or it is a PUT or DELETE request to a resource that does not
+                        * correspond to a file. Check authorization. */
+                       if (!check_authorization(conn, path)) {
+                               send_authorization_request(conn);
+                               return;
+                       }
+               }
+
+               /* request is authorized or does not need authorization */
+
+               /* 7. check if there are request handlers for this uri */
+               if (is_callback_resource) {
+                       if (!is_websocket_request) {
+                               i = callback_handler(conn, callback_data);
+                               if (i > 0) {
+                                       /* Do nothing, callback has served the request. Store
+                                        * the
+                                        * return value as status code for the log and discard
+                                        * all
+                                        * data from the client not used by the callback. */
+                                       conn->status_code = i;
+                                       discard_unread_request_data(conn);
+                               } else {
+                                       /* TODO (high): what if the handler did NOT handle the
+                                        * request */
+                                       /* The last version did handle this as a file request,
+                                        * but
+                                        * since a file request is not always a script resource,
+                                        * the authorization check might be different */
+                                       interpret_uri(conn,
+                                                     path,
+                                                     sizeof(path),
+                                                     &file,
+                                                     &is_found,
+                                                     &is_script_resource,
+                                                     &is_websocket_request,
+                                                     &is_put_or_delete_request);
+                                       callback_handler = NULL;
+
+                                       /* TODO (very low): goto is deprecated but for the
+                                        * moment,
+                                        * a goto is simpler than some curious loop. */
+                                       /* The situation "callback does not handle the request"
+                                        * needs to be reconsidered anyway. */
+                                       goto no_callback_resource;
+                               }
+                       } else {
+#if defined(USE_WEBSOCKET)
+                               handle_websocket_request(conn,
+                                                        path,
+                                                        is_callback_resource,
+                                                        ws_connect_handler,
+                                                        ws_ready_handler,
+                                                        ws_data_handler,
+                                                        ws_close_handler,
+                                                        callback_data);
+#endif
+                       }
+                       return;
+               }
+
+/* 8. handle websocket requests */
+#if defined(USE_WEBSOCKET)
+               if (is_websocket_request) {
+                       if (is_script_resource) {
+                               /* Websocket Lua script */
+                               handle_websocket_request(conn,
+                                                        path,
+                                                        0 /* Lua Script */,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        &conn->ctx->callbacks);
+                       } else {
+#if defined(MG_LEGACY_INTERFACE)
+                               handle_websocket_request(
+                                   conn,
+                                   path,
+                                   !is_script_resource /* could be deprecated global callback */,
+                                   deprecated_websocket_connect_wrapper,
+                                   deprecated_websocket_ready_wrapper,
+                                   deprecated_websocket_data_wrapper,
+                                   NULL,
+                                   &conn->ctx->callbacks);
+#else
+                               send_http_error(conn, 404, "%s", "Not found");
+#endif
+                       }
+                       return;
+               } else
+#endif
+
+#if defined(NO_FILES)
+                       /* 9a. In case the server uses only callbacks, this uri is
+                        * unknown.
+                        * Then, all request handling ends here. */
+                       send_http_error(conn, 404, "%s", "Not Found");
+
+#else
+               /* 9b. This request is either for a static file or resource handled
+                * by a script file. Thus, a DOCUMENT_ROOT must exist. */
+               if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
+                       send_http_error(conn, 404, "%s", "Not Found");
+                       return;
+               }
+
+               /* 10. File is handled by a script. */
+               if (is_script_resource) {
+                       handle_file_based_request(conn, path, &file);
+                       return;
+               }
+
+               /* 11. Handle put/delete/mkcol requests */
+               if (is_put_or_delete_request) {
+                       /* 11.1. PUT method */
+                       if (!strcmp(ri->request_method, "PUT")) {
+                               put_file(conn, path);
+                               return;
+                       }
+                       /* 11.2. DELETE method */
+                       if (!strcmp(ri->request_method, "DELETE")) {
+                               delete_file(conn, path);
+                               return;
+                       }
+                       /* 11.3. MKCOL method */
+                       if (!strcmp(ri->request_method, "MKCOL")) {
+                               mkcol(conn, path);
+                               return;
+                       }
+                       /* 11.4. PATCH method
+                        * This method is not supported for static resources,
+                        * only for scripts (Lua, CGI) and callbacks. */
+                       send_http_error(conn,
+                                       405,
+                                       "%s method not allowed",
+                                       conn->request_info.request_method);
+                       return;
+               }
+
+               /* 11. File does not exist, or it was configured that it should be
+                * hidden */
+               if (!is_found || (must_hide_file(conn, path))) {
+                       send_http_error(conn, 404, "%s", "Not found");
+                       return;
+               }
+
+               /* 12. Directory uris should end with a slash */
+               if (file.is_directory && ri->local_uri[uri_len - 1] != '/') {
+                       gmt_time_string(date, sizeof(date), &curtime);
+                       mg_printf(conn,
+                                 "HTTP/1.1 301 Moved Permanently\r\n"
+                                 "Location: %s/\r\n"
+                                 "Date: %s\r\n"
+                                 /* "Cache-Control: private\r\n" (= default) */
+                                 "Content-Length: 0\r\n"
+                                 "Connection: %s\r\n\r\n",
+                                 ri->request_uri,
+                                 date,
+                                 suggest_connection_header(conn));
+                       return;
+               }
+
+               /* 13. Handle other methods than GET/HEAD */
+               /* 13.1. Handle PROPFIND */
+               if (!strcmp(ri->request_method, "PROPFIND")) {
+                       handle_propfind(conn, path, &file);
+                       return;
+               }
+               /* 13.2. Handle OPTIONS for files */
+               if (!strcmp(ri->request_method, "OPTIONS")) {
+                       /* This standard handler is only used for real files.
+                        * Scripts should support the OPTIONS method themselves, to allow a
+                        * maximum flexibility.
+                        * Lua and CGI scripts may fully support CORS this way (including
+                        * preflights). */
+                       send_options(conn);
+                       return;
+               }
+               /* 13.3. everything but GET and HEAD (e.g. POST) */
+               if (0 != strcmp(ri->request_method, "GET")
+                   && 0 != strcmp(ri->request_method, "HEAD")) {
+                       send_http_error(conn,
+                                       405,
+                                       "%s method not allowed",
+                                       conn->request_info.request_method);
+                       return;
+               }
+
+               /* 14. directories */
+               if (file.is_directory) {
+                       if (substitute_index_file(conn, path, sizeof(path), &file)) {
+                               /* 14.1. use a substitute file */
+                               /* TODO (high): substitute index may be a script resource.
+                                * define what should be possible in this case. */
+                       } else {
+                               /* 14.2. no substitute file */
+                               if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING],
+                                                  "yes")) {
+                                       handle_directory_request(conn, path);
+                               } else {
+                                       send_http_error(conn,
+                                                       403,
+                                                       "%s",
+                                                       "Error: Directory listing denied");
+                               }
+                               return;
+                       }
+               }
+
+               handle_file_based_request(conn, path, &file);
+#endif /* !defined(NO_FILES) */
+
+#if 0
+            /* Perform redirect and auth checks before calling begin_request()
+             * handler.
+             * Otherwise, begin_request() would need to perform auth checks and
+             * redirects. */
+#endif
+       }
+       return;
+}
+
+
+static void
+handle_file_based_request(struct mg_connection *conn,
+                          const char *path,
+                          struct file *file)
+{
+       if (!conn || !conn->ctx) {
+               return;
+       }
+
+       if (0) {
+#ifdef USE_LUA
+       } else if (match_prefix(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
+                               strlen(
+                                   conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
+                               path) > 0) {
+               /* Lua server page: an SSI like page containing mostly plain html
+                * code
+                * plus some tags with server generated contents. */
+               handle_lsp_request(conn, path, file, NULL);
+       } else if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+                               strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+                               path) > 0) {
+               /* Lua in-server module script: a CGI like script used to generate
+                * the
+                * entire reply. */
+               mg_exec_lua_script(conn, path, NULL);
+#endif
+#if defined(USE_DUKTAPE)
+       } else if (match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+                               strlen(
+                                   conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+                               path) > 0) {
+               /* Call duktape to generate the page */
+               mg_exec_duktape_script(conn, path);
+#endif
+#if !defined(NO_CGI)
+       } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+                               strlen(conn->ctx->config[CGI_EXTENSIONS]),
+                               path) > 0) {
+               /* CGI scripts may support all HTTP methods */
+               handle_cgi_request(conn, path);
+#endif /* !NO_CGI */
+       } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
+                               strlen(conn->ctx->config[SSI_EXTENSIONS]),
+                               path) > 0) {
+               handle_ssi_file_request(conn, path, file);
+#if !defined(NO_CACHING)
+       } else if ((!conn->in_error_handler) && is_not_modified(conn, file)) {
+               /* Send 304 "Not Modified" - this must not send any body data */
+               send_http_error(conn, 304, "%s", "");
+#endif /* !NO_CACHING */
+       } else {
+               handle_static_file_request(conn, path, file, NULL);
+       }
+}
+
+
+static void
+close_all_listening_sockets(struct mg_context *ctx)
+{
+       unsigned int i;
+       if (!ctx) {
+               return;
+       }
+
+       for (i = 0; i < ctx->num_listening_sockets; i++) {
+               closesocket(ctx->listening_sockets[i].sock);
+               ctx->listening_sockets[i].sock = INVALID_SOCKET;
+       }
+       mg_free(ctx->listening_sockets);
+       ctx->listening_sockets = NULL;
+       mg_free(ctx->listening_ports);
+       ctx->listening_ports = NULL;
+}
+
+
+/* Valid listening port specification is: [ip_address:]port[s]
+ * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
+ * Examples for IPv6: [::]:80, [::1]:80,
+ *   [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:443s
+ *   see https://tools.ietf.org/html/rfc3513#section-2.2 */
+static int
+parse_port_string(const struct vec *vec, struct socket *so)
+{
+       unsigned int a, b, c, d, port;
+       int ch, len;
+#if defined(USE_IPV6)
+       char buf[100] = {0};
+#endif
+
+       /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
+        * Also, all-zeroes in the socket address means binding to all addresses
+        * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
+       memset(so, 0, sizeof(*so));
+       so->lsa.sin.sin_family = AF_INET;
+
+       if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len)
+           == 5) {
+               /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
+               so->lsa.sin.sin_addr.s_addr =
+                   htonl((a << 24) | (b << 16) | (c << 8) | d);
+               so->lsa.sin.sin_port = htons((uint16_t)port);
+#if defined(USE_IPV6)
+       } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
+                  && mg_inet_pton(
+                         AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) {
+               /* IPv6 address, examples: see above */
+               /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
+                */
+               so->lsa.sin6.sin6_port = htons((uint16_t)port);
+#endif
+       } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) {
+               /* If only port is specified, bind to IPv4, INADDR_ANY */
+               so->lsa.sin.sin_port = htons((uint16_t)port);
+       } else {
+               /* Parsing failure. Make port invalid. */
+               port = 0;
+               len = 0;
+       }
+
+       /* sscanf and the option splitting code ensure the following condition
+        */
+       if ((len < 0) && ((unsigned)len > (unsigned)vec->len)) {
+               return 0;
+       }
+       ch = vec->ptr[len]; /* Next character after the port number */
+       so->is_ssl = (ch == 's');
+       so->ssl_redir = (ch == 'r');
+
+       /* Make sure the port is valid and vector ends with 's', 'r' or ',' */
+       return is_valid_port(port)
+              && (ch == '\0' || ch == 's' || ch == 'r' || ch == ',');
+}
+
+
+static int
+set_ports_option(struct mg_context *ctx)
+{
+       const char *list;
+       int on = 1;
+#if defined(USE_IPV6)
+       int off = 0;
+#endif
+       struct vec vec;
+       struct socket so, *ptr;
+
+       in_port_t *portPtr;
+       union usa usa;
+       socklen_t len;
+
+       int portsTotal = 0;
+       int portsOk = 0;
+
+       if (!ctx) {
+               return 0;
+       }
+
+       memset(&so, 0, sizeof(so));
+       memset(&usa, 0, sizeof(usa));
+       len = sizeof(usa);
+       list = ctx->config[LISTENING_PORTS];
+       while ((list = next_option(list, &vec, NULL)) != NULL) {
+
+               portsTotal++;
+
+               if (!parse_port_string(&vec, &so)) {
+                       mg_cry(fc(ctx),
+                              "%.*s: invalid port spec (entry %i). Expecting list of: %s",
+                              (int)vec.len,
+                              vec.ptr,
+                              portsTotal,
+                              "[IP_ADDRESS:]PORT[s|r]");
+                       continue;
+               }
+
+               if (so.is_ssl && ctx->ssl_ctx == NULL) {
+
+                       mg_cry(fc(ctx),
+                              "Cannot add SSL socket (entry %i). Is -ssl_certificate "
+                              "option set?",
+                              portsTotal);
+                       continue;
+               }
+
+               if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))
+                   == INVALID_SOCKET) {
+
+                       mg_cry(fc(ctx), "cannot create socket (entry %i)", portsTotal);
+                       continue;
+               }
+
+#ifdef _WIN32
+               /* Windows SO_REUSEADDR lets many procs binds to a
+                * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
+                * if someone already has the socket -- DTL */
+               /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
+                * Windows might need a few seconds before
+                * the same port can be used again in the
+                * same process, so a short Sleep may be
+                * required between mg_stop and mg_start.
+                */
+               if (setsockopt(so.sock,
+                              SOL_SOCKET,
+                              SO_EXCLUSIVEADDRUSE,
+                              (SOCK_OPT_TYPE)&on,
+                              sizeof(on)) != 0) {
+
+                       mg_cry(fc(ctx),
+                              "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
+                              portsTotal);
+               }
+#else
+               if (setsockopt(so.sock,
+                              SOL_SOCKET,
+                              SO_REUSEADDR,
+                              (SOCK_OPT_TYPE)&on,
+                              sizeof(on)) != 0) {
+
+                       mg_cry(fc(ctx),
+                              "cannot set socket option SO_REUSEADDR (entry %i)",
+                              portsTotal);
+               }
+#endif
+
+#if defined(USE_IPV6)
+               if (so.lsa.sa.sa_family == AF_INET6
+                   && setsockopt(so.sock,
+                                 IPPROTO_IPV6,
+                                 IPV6_V6ONLY,
+                                 (void *)&off,
+                                 sizeof(off)) != 0) {
+
+                       mg_cry(fc(ctx),
+                              "cannot set socket option IPV6_V6ONLY (entry %i)",
+                              portsTotal);
+               }
+#endif
+
+               if (so.lsa.sa.sa_family == AF_INET) {
+
+                       len = sizeof(so.lsa.sin);
+                       if (bind(so.sock, &so.lsa.sa, len) != 0) {
+                               mg_cry(fc(ctx),
+                                      "cannot bind to %.*s: %d (%s)",
+                                      (int)vec.len,
+                                      vec.ptr,
+                                      (int)ERRNO,
+                                      strerror(errno));
+                               closesocket(so.sock);
+                               so.sock = INVALID_SOCKET;
+                               continue;
+                       }
+               }
+#if defined(USE_IPV6)
+               else if (so.lsa.sa.sa_family == AF_INET6) {
+
+                       len = sizeof(so.lsa.sin6);
+                       if (bind(so.sock, &so.lsa.sa, len) != 0) {
+                               mg_cry(fc(ctx),
+                                      "cannot bind to IPv6 %.*s: %d (%s)",
+                                      (int)vec.len,
+                                      vec.ptr,
+                                      (int)ERRNO,
+                                      strerror(errno));
+                               closesocket(so.sock);
+                               so.sock = INVALID_SOCKET;
+                               continue;
+                       }
+               }
+#endif
+               else {
+                       mg_cry(fc(ctx),
+                              "cannot bind: address family not supported (entry %i)",
+                              portsTotal);
+                       continue;
+               }
+
+               if (listen(so.sock, SOMAXCONN) != 0) {
+
+                       mg_cry(fc(ctx),
+                              "cannot listen to %.*s: %d (%s)",
+                              (int)vec.len,
+                              vec.ptr,
+                              (int)ERRNO,
+                              strerror(errno));
+                       closesocket(so.sock);
+                       so.sock = INVALID_SOCKET;
+                       continue;
+               }
+
+               if (getsockname(so.sock, &(usa.sa), &len) != 0) {
+
+                       int err = (int)ERRNO;
+                       mg_cry(fc(ctx),
+                              "call to getsockname failed %.*s: %d (%s)",
+                              (int)vec.len,
+                              vec.ptr,
+                              err,
+                              strerror(errno));
+                       closesocket(so.sock);
+                       so.sock = INVALID_SOCKET;
+                       continue;
+               }
+
+               if ((ptr = (struct socket *)
+                        mg_realloc(ctx->listening_sockets,
+                                   (ctx->num_listening_sockets + 1)
+                                       * sizeof(ctx->listening_sockets[0]))) == NULL) {
+
+                       mg_cry(fc(ctx), "%s", "Out of memory");
+                       closesocket(so.sock);
+                       so.sock = INVALID_SOCKET;
+                       continue;
+               }
+
+               if ((portPtr =
+                        (in_port_t *)mg_realloc(ctx->listening_ports,
+                                                (ctx->num_listening_sockets + 1)
+                                                    * sizeof(ctx->listening_ports[0])))
+                   == NULL) {
+
+                       mg_cry(fc(ctx), "%s", "Out of memory");
+                       closesocket(so.sock);
+                       so.sock = INVALID_SOCKET;
+                       mg_free(ptr);
+                       continue;
+               }
+
+               set_close_on_exec(so.sock, fc(ctx));
+               ctx->listening_sockets = ptr;
+               ctx->listening_sockets[ctx->num_listening_sockets] = so;
+               ctx->listening_ports = portPtr;
+               ctx->listening_ports[ctx->num_listening_sockets] =
+                   ntohs(usa.sin.sin_port);
+               ctx->num_listening_sockets++;
+               portsOk++;
+       }
+
+       if (portsOk != portsTotal) {
+               close_all_listening_sockets(ctx);
+               portsOk = 0;
+       }
+
+       return portsOk;
+}
+
+
+static const char *
+header_val(const struct mg_connection *conn, const char *header)
+{
+       const char *header_value;
+
+       if ((header_value = mg_get_header(conn, header)) == NULL) {
+               return "-";
+       } else {
+               return header_value;
+       }
+}
+
+
+static void
+log_access(const struct mg_connection *conn)
+{
+       const struct mg_request_info *ri;
+       struct file fi;
+       char date[64], src_addr[IP_ADDR_STR_LEN];
+       struct tm *tm;
+
+       const char *referer;
+       const char *user_agent;
+
+       char buf[4096];
+
+       if (!conn || !conn->ctx) {
+               return;
+       }
+
+       if (conn->ctx->config[ACCESS_LOG_FILE] != NULL) {
+               if (mg_fopen(conn, conn->ctx->config[ACCESS_LOG_FILE], "a+", &fi)
+                   == 0) {
+                       fi.fp = NULL;
+               }
+       } else {
+               fi.fp = NULL;
+       }
+
+       if (fi.fp == NULL && conn->ctx->callbacks.log_message == NULL) {
+               return;
+       }
+
+       tm = localtime(&conn->conn_birth_time);
+       if (tm != NULL) {
+               strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
+       } else {
+               mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
+               date[sizeof(date) - 1] = '\0';
+       }
+
+       ri = &conn->request_info;
+
+       sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+       referer = header_val(conn, "Referer");
+       user_agent = header_val(conn, "User-Agent");
+
+       mg_snprintf(conn,
+                   NULL, /* Ignore truncation in access log */
+                   buf,
+                   sizeof(buf),
+                   "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT " %s %s",
+                   src_addr,
+                   ri->remote_user == NULL ? "-" : ri->remote_user,
+                   date,
+                   ri->request_method ? ri->request_method : "-",
+                   ri->request_uri ? ri->request_uri : "-",
+                   ri->query_string ? "?" : "",
+                   ri->query_string ? ri->query_string : "",
+                   ri->http_version,
+                   conn->status_code,
+                   conn->num_bytes_sent,
+                   referer,
+                   user_agent);
+
+       if (conn->ctx->callbacks.log_access) {
+               conn->ctx->callbacks.log_access(conn, buf);
+       }
+
+       if (fi.fp) {
+               flockfile(fi.fp);
+               fprintf(fi.fp, "%s\n", buf);
+               fflush(fi.fp);
+               funlockfile(fi.fp);
+               mg_fclose(&fi);
+       }
+}
+
+
+/* Verify given socket address against the ACL.
+ * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
+ */
+static int
+check_acl(struct mg_context *ctx, uint32_t remote_ip)
+{
+       int allowed, flag;
+       uint32_t net, mask;
+       struct vec vec;
+
+       if (ctx) {
+               const char *list = ctx->config[ACCESS_CONTROL_LIST];
+
+               /* If any ACL is set, deny by default */
+               allowed = list == NULL ? '+' : '-';
+
+               while ((list = next_option(list, &vec, NULL)) != NULL) {
+                       flag = vec.ptr[0];
+                       if ((flag != '+' && flag != '-')
+                           || parse_net(&vec.ptr[1], &net, &mask) == 0) {
+                               mg_cry(fc(ctx),
+                                      "%s: subnet must be [+|-]x.x.x.x[/x]",
+                                      __func__);
+                               return -1;
+                       }
+
+                       if (net == (remote_ip & mask)) {
+                               allowed = flag;
+                       }
+               }
+
+               return allowed == '+';
+       }
+       return -1;
+}
+
+
+#if !defined(_WIN32)
+static int
+set_uid_option(struct mg_context *ctx)
+{
+       struct passwd *pw;
+       if (ctx) {
+               const char *uid = ctx->config[RUN_AS_USER];
+               int success = 0;
+
+               if (uid == NULL) {
+                       success = 1;
+               } else {
+                       if ((pw = getpwnam(uid)) == NULL) {
+                               mg_cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
+                       } else if (setgid(pw->pw_gid) == -1) {
+                               mg_cry(fc(ctx),
+                                      "%s: setgid(%s): %s",
+                                      __func__,
+                                      uid,
+                                      strerror(errno));
+                       } else if (setgroups(0, NULL)) {
+                               mg_cry(fc(ctx),
+                                      "%s: setgroups(): %s",
+                                      __func__,
+                                      strerror(errno));
+                       } else if (setuid(pw->pw_uid) == -1) {
+                               mg_cry(fc(ctx),
+                                      "%s: setuid(%s): %s",
+                                      __func__,
+                                      uid,
+                                      strerror(errno));
+                       } else {
+                               success = 1;
+                       }
+               }
+
+               return success;
+       }
+       return 0;
+}
+#endif /* !_WIN32 */
+
+
+static void
+tls_dtor(void *key)
+{
+       struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
+       /* key == pthread_getspecific(sTlsKey); */
+
+       if (tls) {
+               if (tls->is_master == 2) {
+                       tls->is_master = -3; /* Mark memory as dead */
+                       mg_free(tls);
+               }
+       }
+       pthread_setspecific(sTlsKey, NULL);
+}
+
+
+#if !defined(NO_SSL)
+
+/* Must be set if sizeof(pthread_t) > sizeof(unsigned long) */
+static unsigned long
+ssl_id_callback(void)
+{
+#ifdef _WIN32
+       return GetCurrentThreadId();
+#else
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
+ * or not, so one of the two conditions will be unreachable by construction.
+ * Unfortunately the C standard does not define a way to check this at
+ * compile time, since the #if preprocessor conditions can not use the sizeof
+ * operator as an argument. */
+#endif
+
+       if (sizeof(pthread_t) > sizeof(unsigned long)) {
+               /* This is the problematic case for CRYPTO_set_id_callback:
+                * The OS pthread_t can not be cast to unsigned long. */
+               struct mg_workerTLS *tls =
+                   (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+               if (tls == NULL) {
+                       /* SSL called from an unknown thread: Create some thread index.
+                        */
+                       tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
+                       tls->is_master = -2; /* -2 means "3rd party thread" */
+                       tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+                       pthread_setspecific(sTlsKey, tls);
+               }
+               return tls->thread_idx;
+       } else {
+               /* pthread_t may be any data type, so a simple cast to unsigned long
+                * can rise a warning/error, depending on the platform.
+                * Here memcpy is used as an anything-to-anything cast. */
+               unsigned long ret = 0;
+               pthread_t t = pthread_self();
+               memcpy(&ret, &t, sizeof(pthread_t));
+               return ret;
+       }
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+}
+
+
+static int ssl_use_pem_file(struct mg_context *ctx, const char *pem);
+static const char *ssl_error(void);
+
+
+static int
+refresh_trust(struct mg_connection *conn)
+{
+       static int reload_lock = 0;
+       static long int data_check = 0;
+
+       struct stat cert_buf;
+       long int t;
+       char *pem;
+       int should_verify_peer;
+
+       if ((pem = conn->ctx->config[SSL_CERTIFICATE]) == NULL
+           && conn->ctx->callbacks.init_ssl == NULL) {
+               return 0;
+       }
+
+       t = data_check;
+       if (stat(pem, &cert_buf) != -1) {
+               t = (long int)cert_buf.st_mtime;
+       }
+
+       if (data_check != t) {
+               data_check = t;
+
+               should_verify_peer =
+                   (conn->ctx->config[SSL_DO_VERIFY_PEER] != NULL)
+                   && (mg_strcasecmp(conn->ctx->config[SSL_DO_VERIFY_PEER], "yes")
+                       == 0);
+
+               if (should_verify_peer) {
+                       char *ca_path = conn->ctx->config[SSL_CA_PATH];
+                       char *ca_file = conn->ctx->config[SSL_CA_FILE];
+                       if (SSL_CTX_load_verify_locations(conn->ctx->ssl_ctx,
+                                                         ca_file,
+                                                         ca_path) != 1) {
+                               mg_cry(fc(conn->ctx),
+                                      "SSL_CTX_load_verify_locations error: %s "
+                                      "ssl_verify_peer requires setting "
+                                      "either ssl_ca_path or ssl_ca_file. Is any of them "
+                                      "present in "
+                                      "the .conf file?",
+                                      ssl_error());
+                               return 0;
+                       }
+               }
+
+               if (!reload_lock) {
+                       reload_lock = 1;
+                       if (ssl_use_pem_file(conn->ctx, pem) == 0) {
+                               return 0;
+                       }
+                       reload_lock = 0;
+               }
+       }
+       /* lock while cert is reloading */
+       while (reload_lock) {
+               sleep(1);
+       }
+
+       return 1;
+}
+
+
+static pthread_mutex_t *ssl_mutexes;
+
+
+static int
+sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *))
+{
+       int ret, err;
+       int short_trust;
+
+       if (!conn) {
+               return 0;
+       }
+
+       short_trust =
+           (conn->ctx->config[SSL_SHORT_TRUST] != NULL)
+           && (mg_strcasecmp(conn->ctx->config[SSL_SHORT_TRUST], "yes") == 0);
+
+       if (short_trust) {
+               int trust_ret = refresh_trust(conn);
+               if (!trust_ret) {
+                       return trust_ret;
+               }
+       }
+
+       conn->ssl = SSL_new(s);
+       if (conn->ssl == NULL) {
+               return 0;
+       }
+
+       ret = SSL_set_fd(conn->ssl, conn->client.sock);
+       if (ret != 1) {
+               err = SSL_get_error(conn->ssl, ret);
+               (void)err; /* TODO: set some error message */
+               SSL_free(conn->ssl);
+               conn->ssl = NULL;
+               /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+               /* see
+                * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+               ERR_remove_state(0);
+               return 0;
+       }
+
+       ret = func(conn->ssl);
+       if (ret != 1) {
+               err = SSL_get_error(conn->ssl, ret);
+               (void)err; /* TODO: set some error message */
+               SSL_free(conn->ssl);
+               conn->ssl = NULL;
+               /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+               /* see
+                * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+               ERR_remove_state(0);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/* Return OpenSSL error message (from CRYPTO lib) */
+static const char *
+ssl_error(void)
+{
+       unsigned long err;
+       err = ERR_get_error();
+       return err == 0 ? "" : ERR_error_string(err, NULL);
+}
+
+
+static void
+ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
+{
+       (void)line;
+       (void)file;
+
+       if (mode & 1) {
+               /* 1 is CRYPTO_LOCK */
+               (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
+       } else {
+               (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
+       }
+}
+
+
+#if !defined(NO_SSL_DL)
+static void *
+load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw)
+{
+       union {
+               void *p;
+               void (*fp)(void);
+       } u;
+       void *dll_handle;
+       struct ssl_func *fp;
+
+       if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
+               mg_cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
+               return NULL;
+       }
+
+       for (fp = sw; fp->name != NULL; fp++) {
+#ifdef _WIN32
+               /* GetProcAddress() returns pointer to function */
+               u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
+#else
+               /* dlsym() on UNIX returns void *. ISO C forbids casts of data
+                * pointers to function pointers. We need to use a union to make a
+                * cast. */
+               u.p = dlsym(dll_handle, fp->name);
+#endif /* _WIN32 */
+               if (u.fp == NULL) {
+                       mg_cry(fc(ctx),
+                              "%s: %s: cannot find %s",
+                              __func__,
+                              dll_name,
+                              fp->name);
+                       dlclose(dll_handle);
+                       return NULL;
+               } else {
+                       fp->ptr = u.fp;
+               }
+       }
+
+       return dll_handle;
+}
+
+
+static void *ssllib_dll_handle;    /* Store the ssl library handle. */
+static void *cryptolib_dll_handle; /* Store the crypto library handle. */
+
+#endif /* NO_SSL_DL */
+
+
+#if defined(SSL_ALREADY_INITIALIZED)
+static int cryptolib_users = 1; /* Reference counter for crypto library. */
+#else
+static int cryptolib_users = 0; /* Reference counter for crypto library. */
+#endif
+
+
+static int
+initialize_ssl(struct mg_context *ctx)
+{
+       int i;
+       size_t size;
+
+#if !defined(NO_SSL_DL)
+       if (!cryptolib_dll_handle) {
+               cryptolib_dll_handle = load_dll(ctx, CRYPTO_LIB, crypto_sw);
+               if (!cryptolib_dll_handle) {
+                       return 0;
+               }
+       }
+#endif /* NO_SSL_DL */
+
+       if (mg_atomic_inc(&cryptolib_users) > 1) {
+               return 1;
+       }
+
+       /* Initialize locking callbacks, needed for thread safety.
+        * http://www.openssl.org/support/faq.html#PROG1
+        */
+       i = CRYPTO_num_locks();
+       if (i < 0) {
+               i = 0;
+       }
+       size = sizeof(pthread_mutex_t) * ((size_t)(i));
+       if ((ssl_mutexes = (pthread_mutex_t *)mg_malloc(size)) == NULL) {
+               mg_cry(fc(ctx),
+                      "%s: cannot allocate mutexes: %s",
+                      __func__,
+                      ssl_error());
+               return 0;
+       }
+
+       for (i = 0; i < CRYPTO_num_locks(); i++) {
+               pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr);
+       }
+
+       CRYPTO_set_locking_callback(&ssl_locking_callback);
+       CRYPTO_set_id_callback(&ssl_id_callback);
+
+       return 1;
+}
+
+
+static int
+ssl_use_pem_file(struct mg_context *ctx, const char *pem)
+{
+       if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0) {
+               mg_cry(fc(ctx),
+                      "%s: cannot open certificate file %s: %s",
+                      __func__,
+                      pem,
+                      ssl_error());
+               return 0;
+       }
+
+       /* could use SSL_CTX_set_default_passwd_cb_userdata */
+       if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
+               mg_cry(fc(ctx),
+                      "%s: cannot open private key file %s: %s",
+                      __func__,
+                      pem,
+                      ssl_error());
+               return 0;
+       }
+
+       if (SSL_CTX_check_private_key(ctx->ssl_ctx) == 0) {
+               mg_cry(fc(ctx),
+                      "%s: certificate and private key do not match: %s",
+                      __func__,
+                      pem);
+               return 0;
+       }
+
+       if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem) == 0) {
+               mg_cry(fc(ctx),
+                      "%s: cannot use certificate chain file %s: %s",
+                      __func__,
+                      pem,
+                      ssl_error());
+               return 0;
+       }
+       return 1;
+}
+
+
+static long
+ssl_get_protocol(int version_id)
+{
+       long ret = SSL_OP_ALL;
+       if (version_id > 0)
+               ret |= SSL_OP_NO_SSLv2;
+       if (version_id > 1)
+               ret |= SSL_OP_NO_SSLv3;
+       if (version_id > 2)
+               ret |= SSL_OP_NO_TLSv1;
+       if (version_id > 3)
+               ret |= SSL_OP_NO_TLSv1_1;
+       return ret;
+}
+
+
+/* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */
+static int
+set_ssl_option(struct mg_context *ctx)
+{
+       const char *pem;
+       int callback_ret;
+       int should_verify_peer;
+       const char *ca_path;
+       const char *ca_file;
+       int use_default_verify_paths;
+       int verify_depth;
+       time_t now_rt = time(NULL);
+       struct timespec now_mt;
+       md5_byte_t ssl_context_id[16];
+       md5_state_t md5state;
+       int protocol_ver;
+
+       /* If PEM file is not specified and the init_ssl callback
+        * is not specified, skip SSL initialization. */
+       if (!ctx) {
+               return 0;
+       }
+       if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL
+           && ctx->callbacks.init_ssl == NULL) {
+               return 1;
+       }
+
+       if (!initialize_ssl(ctx)) {
+               return 0;
+       }
+
+#if !defined(NO_SSL_DL)
+       if (!ssllib_dll_handle) {
+               ssllib_dll_handle = load_dll(ctx, SSL_LIB, ssl_sw);
+               if (!ssllib_dll_handle) {
+                       return 0;
+               }
+       }
+#endif /* NO_SSL_DL */
+
+       /* Initialize SSL library */
+       SSL_library_init();
+       SSL_load_error_strings();
+
+       if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+               mg_cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
+               return 0;
+       }
+
+       SSL_CTX_clear_options(ctx->ssl_ctx,
+                             SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
+                                 | SSL_OP_NO_TLSv1_1);
+       protocol_ver = atoi(ctx->config[SSL_PROTOCOL_VERSION]);
+       SSL_CTX_set_options(ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
+       SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
+       SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
+
+       /* If a callback has been specified, call it. */
+       callback_ret =
+           (ctx->callbacks.init_ssl == NULL)
+               ? 0
+               : (ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data));
+
+       /* If callback returns 0, civetweb sets up the SSL certificate.
+        * If it returns 1, civetweb assumes the calback already did this.
+        * If it returns -1, initializing ssl fails. */
+       if (callback_ret < 0) {
+               mg_cry(fc(ctx), "SSL callback returned error: %i", callback_ret);
+               return 0;
+       }
+       if (callback_ret > 0) {
+               if (pem != NULL) {
+                       (void)SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
+               }
+               return 1;
+       }
+
+       /* Use some UID as session context ID. */
+       md5_init(&md5state);
+       md5_append(&md5state, (const md5_byte_t *)&now_rt, sizeof(now_rt));
+       clock_gettime(CLOCK_MONOTONIC, &now_mt);
+       md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
+       md5_append(&md5state,
+                  (const md5_byte_t *)ctx->config[LISTENING_PORTS],
+                  strlen(ctx->config[LISTENING_PORTS]));
+       md5_append(&md5state, (const md5_byte_t *)ctx, sizeof(*ctx));
+       md5_finish(&md5state, ssl_context_id);
+
+       SSL_CTX_set_session_id_context(ctx->ssl_ctx,
+                                      (const unsigned char *)&ssl_context_id,
+                                      sizeof(ssl_context_id));
+
+       if (pem != NULL) {
+               if (!ssl_use_pem_file(ctx, pem)) {
+                       return 0;
+               }
+       }
+
+       should_verify_peer =
+           (ctx->config[SSL_DO_VERIFY_PEER] != NULL)
+           && (mg_strcasecmp(ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0);
+
+       use_default_verify_paths =
+           (ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
+           && (mg_strcasecmp(ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes") == 0);
+
+       if (should_verify_peer) {
+               ca_path = ctx->config[SSL_CA_PATH];
+               ca_file = ctx->config[SSL_CA_FILE];
+               if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_path)
+                   != 1) {
+                       mg_cry(fc(ctx),
+                              "SSL_CTX_load_verify_locations error: %s "
+                              "ssl_verify_peer requires setting "
+                              "either ssl_ca_path or ssl_ca_file. Is any of them "
+                              "present in "
+                              "the .conf file?",
+                              ssl_error());
+                       return 0;
+               }
+
+               SSL_CTX_set_verify(ctx->ssl_ctx,
+                                  SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                                  NULL);
+
+               if (use_default_verify_paths
+                   && SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1) {
+                       mg_cry(fc(ctx),
+                              "SSL_CTX_set_default_verify_paths error: %s",
+                              ssl_error());
+                       return 0;
+               }
+
+               if (ctx->config[SSL_VERIFY_DEPTH]) {
+                       verify_depth = atoi(ctx->config[SSL_VERIFY_DEPTH]);
+                       SSL_CTX_set_verify_depth(ctx->ssl_ctx, verify_depth);
+               }
+       }
+
+       if (ctx->config[SSL_CIPHER_LIST] != NULL) {
+               if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config[SSL_CIPHER_LIST])
+                   != 1) {
+                       mg_cry(fc(ctx), "SSL_CTX_set_cipher_list error: %s", ssl_error());
+               }
+       }
+
+       return 1;
+}
+
+
+static void
+uninitialize_ssl(struct mg_context *ctx)
+{
+       int i;
+       (void)ctx;
+
+       if (mg_atomic_dec(&cryptolib_users) == 0) {
+
+               /* Shutdown according to
+                * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
+                * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
+                */
+               CRYPTO_set_locking_callback(NULL);
+               CRYPTO_set_id_callback(NULL);
+               ENGINE_cleanup();
+               CONF_modules_unload(1);
+               ERR_free_strings();
+               EVP_cleanup();
+               CRYPTO_cleanup_all_ex_data();
+               ERR_remove_state(0);
+
+               for (i = 0; i < CRYPTO_num_locks(); i++) {
+                       pthread_mutex_destroy(&ssl_mutexes[i]);
+               }
+               mg_free(ssl_mutexes);
+               ssl_mutexes = NULL;
+       }
+}
+#endif /* !NO_SSL */
+
+
+static int
+set_gpass_option(struct mg_context *ctx)
+{
+       if (ctx) {
+               struct file file = STRUCT_FILE_INITIALIZER;
+               const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
+               if (path != NULL && !mg_stat(fc(ctx), path, &file)) {
+                       mg_cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
+                       return 0;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+
+static int
+set_acl_option(struct mg_context *ctx)
+{
+       return check_acl(ctx, (uint32_t)0x7f000001UL) != -1;
+}
+
+
+static void
+reset_per_request_attributes(struct mg_connection *conn)
+{
+       if (!conn) {
+               return;
+       }
+       conn->path_info = NULL;
+       conn->num_bytes_sent = conn->consumed_content = 0;
+       conn->status_code = -1;
+       conn->is_chunked = 0;
+       conn->must_close = conn->request_len = conn->throttle = 0;
+       conn->request_info.content_length = -1;
+       conn->request_info.remote_user = NULL;
+       conn->request_info.request_method = NULL;
+       conn->request_info.request_uri = NULL;
+       conn->request_info.local_uri = NULL;
+       conn->request_info.uri = NULL; /* TODO: cleanup uri,
+                                       * local_uri and request_uri */
+       conn->request_info.http_version = NULL;
+       conn->request_info.num_headers = 0;
+       conn->data_len = 0;
+       conn->chunk_remainder = 0;
+       conn->internal_error = 0;
+}
+
+
+static int
+set_sock_timeout(SOCKET sock, int milliseconds)
+{
+       int r0 = 0, r1, r2;
+
+#ifdef _WIN32
+       /* Windows specific */
+
+       DWORD tv = (DWORD)milliseconds;
+
+#else
+       /* Linux, ... (not Windows) */
+
+       struct timeval tv;
+
+/* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
+ * max. time waiting for the acknowledged of TCP data before the connection
+ * will be forcefully closed and ETIMEDOUT is returned to the application.
+ * If this option is not set, the default timeout of 20-30 minutes is used.
+*/
+/* #define TCP_USER_TIMEOUT (18) */
+
+#if defined(TCP_USER_TIMEOUT)
+       unsigned int uto = (unsigned int)milliseconds;
+       r0 = setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
+#endif
+
+       memset(&tv, 0, sizeof(tv));
+       tv.tv_sec = milliseconds / 1000;
+       tv.tv_usec = (milliseconds * 1000) % 1000000;
+
+#endif /* _WIN32 */
+
+       r1 = setsockopt(
+           sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
+       r2 = setsockopt(
+           sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
+
+       return r0 || r1 || r2;
+}
+
+
+static int
+set_tcp_nodelay(SOCKET sock, int nodelay_on)
+{
+       if (setsockopt(sock,
+                      IPPROTO_TCP,
+                      TCP_NODELAY,
+                      (SOCK_OPT_TYPE)&nodelay_on,
+                      sizeof(nodelay_on)) != 0) {
+               /* Error */
+               return 1;
+       }
+       /* OK */
+       return 0;
+}
+
+
+static void
+close_socket_gracefully(struct mg_connection *conn)
+{
+#if defined(_WIN32)
+       char buf[MG_BUF_LEN];
+       int n;
+#endif
+       struct linger linger;
+
+       if (!conn) {
+               return;
+       }
+
+       /* Set linger option to avoid socket hanging out after close. This
+        * prevent
+        * ephemeral port exhaust problem under high QPS. */
+       linger.l_onoff = 1;
+       linger.l_linger = 1;
+
+       if (setsockopt(conn->client.sock,
+                      SOL_SOCKET,
+                      SO_LINGER,
+                      (char *)&linger,
+                      sizeof(linger)) != 0) {
+               mg_cry(conn,
+                      "%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s",
+                      __func__,
+                      strerror(ERRNO));
+       }
+
+       /* Send FIN to the client */
+       shutdown(conn->client.sock, SHUT_WR);
+       set_non_blocking_mode(conn->client.sock);
+
+#if defined(_WIN32)
+       /* Read and discard pending incoming data. If we do not do that and
+        * close
+        * the socket, the data in the send buffer may be discarded. This
+        * behaviour is seen on Windows, when client keeps sending data
+        * when server decides to close the connection; then when client
+        * does recv() it gets no data back. */
+       do {
+               n = pull(
+                   NULL, conn, buf, sizeof(buf), 1E-10 /* TODO: allow 0 as timeout */);
+       } while (n > 0);
+#endif
+
+       /* Now we know that our FIN is ACK-ed, safe to close */
+       closesocket(conn->client.sock);
+       conn->client.sock = INVALID_SOCKET;
+}
+
+
+static void
+close_connection(struct mg_connection *conn)
+{
+       if (!conn || !conn->ctx) {
+               return;
+       }
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       if (conn->lua_websocket_state) {
+               lua_websocket_close(conn, conn->lua_websocket_state);
+               conn->lua_websocket_state = NULL;
+       }
+#endif
+
+       /* call the connection_close callback if assigned */
+       if ((conn->ctx->callbacks.connection_close != NULL)
+           && (conn->ctx->context_type == 1)) {
+               conn->ctx->callbacks.connection_close(conn);
+       }
+
+       mg_lock_connection(conn);
+
+       conn->must_close = 1;
+
+#ifndef NO_SSL
+       if (conn->ssl != NULL) {
+               /* Run SSL_shutdown twice to ensure completly close SSL connection
+                */
+               SSL_shutdown(conn->ssl);
+               SSL_free(conn->ssl);
+               /* maybe not? CRYPTO_cleanup_all_ex_data(); */
+               /* see
+                * https://wiki.openssl.org/index.php/Talk:Library_Initialization */
+               ERR_remove_state(0);
+               conn->ssl = NULL;
+       }
+#endif
+       if (conn->client.sock != INVALID_SOCKET) {
+               close_socket_gracefully(conn);
+               conn->client.sock = INVALID_SOCKET;
+       }
+
+       mg_unlock_connection(conn);
+}
+
+void
+mg_close_connection(struct mg_connection *conn)
+{
+       struct mg_context *client_ctx = NULL;
+       unsigned int i;
+
+       if (conn == NULL) {
+               return;
+       }
+
+       if (conn->ctx->context_type == 2) {
+               client_ctx = conn->ctx;
+               /* client context: loops must end */
+               conn->ctx->stop_flag = 1;
+       }
+
+#ifndef NO_SSL
+       if (conn->client_ssl_ctx != NULL) {
+               SSL_CTX_free((SSL_CTX *)conn->client_ssl_ctx);
+       }
+#endif
+       close_connection(conn);
+       if (client_ctx != NULL) {
+               /* join worker thread and free context */
+               for (i = 0; i < client_ctx->cfg_worker_threads; i++) {
+                       if (client_ctx->workerthreadids[i] != 0) {
+                               mg_join_thread(client_ctx->workerthreadids[i]);
+                       }
+               }
+               mg_free(client_ctx->workerthreadids);
+               mg_free(client_ctx);
+               (void)pthread_mutex_destroy(&conn->mutex);
+               mg_free(conn);
+       }
+}
+
+
+static struct mg_connection *
+mg_connect_client_impl(const struct mg_client_options *client_options,
+                       int use_ssl,
+                       char *ebuf,
+                       size_t ebuf_len)
+{
+       static struct mg_context fake_ctx;
+       struct mg_connection *conn = NULL;
+       SOCKET sock;
+       union usa sa;
+
+       if (!connect_socket(&fake_ctx,
+                           client_options->host,
+                           client_options->port,
+                           use_ssl,
+                           ebuf,
+                           ebuf_len,
+                           &sock,
+                           &sa)) {
+               ;
+       } else if ((conn = (struct mg_connection *)
+                       mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "calloc(): %s",
+                           strerror(ERRNO));
+               closesocket(sock);
+#ifndef NO_SSL
+       } else if (use_ssl
+                  && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
+                         == NULL) {
+               mg_snprintf(NULL,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "SSL_CTX_new error");
+               closesocket(sock);
+               mg_free(conn);
+               conn = NULL;
+#endif /* NO_SSL */
+
+       } else {
+
+#ifdef USE_IPV6
+               socklen_t len = (sa.sa.sa_family == AF_INET)
+                                   ? sizeof(conn->client.rsa.sin)
+                                   : sizeof(conn->client.rsa.sin6);
+               struct sockaddr *psa =
+                   (sa.sa.sa_family == AF_INET)
+                       ? (struct sockaddr *)&(conn->client.rsa.sin)
+                       : (struct sockaddr *)&(conn->client.rsa.sin6);
+#else
+               socklen_t len = sizeof(conn->client.rsa.sin);
+               struct sockaddr *psa = (struct sockaddr *)&(conn->client.rsa.sin);
+#endif
+
+               conn->buf_size = MAX_REQUEST_SIZE;
+               conn->buf = (char *)(conn + 1);
+               conn->ctx = &fake_ctx;
+               conn->client.sock = sock;
+               conn->client.lsa = sa;
+
+               if (getsockname(sock, psa, &len) != 0) {
+                       mg_cry(conn,
+                              "%s: getsockname() failed: %s",
+                              __func__,
+                              strerror(ERRNO));
+               }
+
+               conn->client.is_ssl = use_ssl ? 1 : 0;
+               (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
+
+#ifndef NO_SSL
+               if (use_ssl) {
+                       fake_ctx.ssl_ctx = conn->client_ssl_ctx;
+
+                       /* TODO: Check ssl_verify_peer and ssl_ca_path here.
+                        * SSL_CTX_set_verify call is needed to switch off server
+                        * certificate checking, which is off by default in OpenSSL and
+                        * on in yaSSL. */
+                       /* TODO: SSL_CTX_set_verify(conn->client_ssl_ctx,
+                        * SSL_VERIFY_PEER, verify_ssl_server); */
+
+                       if (client_options->client_cert) {
+                               if (!ssl_use_pem_file(&fake_ctx, client_options->client_cert)) {
+                                       mg_snprintf(NULL,
+                                                   NULL, /* No truncation check for ebuf */
+                                                   ebuf,
+                                                   ebuf_len,
+                                                   "Can not use SSL client certificate");
+                                       SSL_CTX_free(conn->client_ssl_ctx);
+                                       closesocket(sock);
+                                       mg_free(conn);
+                                       conn = NULL;
+                               }
+                       }
+
+                       if (client_options->server_cert) {
+                               SSL_CTX_load_verify_locations(conn->client_ssl_ctx,
+                                                             client_options->server_cert,
+                                                             NULL);
+                               SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, NULL);
+                       } else {
+                               SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL);
+                       }
+
+                       if (!sslize(conn, conn->client_ssl_ctx, SSL_connect)) {
+                               mg_snprintf(NULL,
+                                           NULL, /* No truncation check for ebuf */
+                                           ebuf,
+                                           ebuf_len,
+                                           "SSL connection error");
+                               SSL_CTX_free(conn->client_ssl_ctx);
+                               closesocket(sock);
+                               mg_free(conn);
+                               conn = NULL;
+                       }
+               }
+#endif
+       }
+
+       return conn;
+}
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+                         char *error_buffer,
+                         size_t error_buffer_size)
+{
+       return mg_connect_client_impl(client_options,
+                                     1,
+                                     error_buffer,
+                                     error_buffer_size);
+}
+
+
+struct mg_connection *
+mg_connect_client(const char *host,
+                  int port,
+                  int use_ssl,
+                  char *error_buffer,
+                  size_t error_buffer_size)
+{
+       struct mg_client_options opts;
+       memset(&opts, 0, sizeof(opts));
+       opts.host = host;
+       opts.port = port;
+       return mg_connect_client_impl(&opts,
+                                     use_ssl,
+                                     error_buffer,
+                                     error_buffer_size);
+}
+
+
+static const struct {
+       const char *proto;
+       size_t proto_len;
+       unsigned default_port;
+} abs_uri_protocols[] = {{"http://", 7, 80},
+                         {"https://", 8, 443},
+                         {"ws://", 5, 80},
+                         {"wss://", 6, 443},
+                         {NULL, 0, 0}};
+
+
+/* Check if the uri is valid.
+ * return 0 for invalid uri,
+ * return 1 for *,
+ * return 2 for relative uri,
+ * return 3 for absolute uri without port,
+ * return 4 for absolute uri with port */
+static int
+get_uri_type(const char *uri)
+{
+       int i;
+       char *hostend, *portbegin, *portend;
+       unsigned long port;
+
+       /* According to the HTTP standard
+        * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
+        * URI can be an asterisk (*) or should start with slash (relative uri),
+        * or it should start with the protocol (absolute uri). */
+       if (uri[0] == '*' && uri[1] == '\0') {
+               /* asterisk */
+               return 1;
+       }
+       if (uri[0] == '/') {
+               /* relative uri */
+               return 2;
+       }
+
+       /* It could be an absolute uri: */
+       /* This function only checks if the uri is valid, not if it is
+        * addressing the current server. So civetweb can also be used
+        * as a proxy server. */
+       for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
+               if (mg_strncasecmp(uri,
+                                  abs_uri_protocols[i].proto,
+                                  abs_uri_protocols[i].proto_len) == 0) {
+
+                       hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
+                       if (!hostend) {
+                               return 0;
+                       }
+                       portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':');
+                       if (!portbegin) {
+                               return 3;
+                       }
+
+                       port = strtoul(portbegin + 1, &portend, 10);
+                       if ((portend != hostend) || !port || !is_valid_port(port)) {
+                               return 0;
+                       }
+
+                       return 4;
+               }
+       }
+
+       return 0;
+}
+
+
+/* Return NULL or the relative uri at the current server */
+static const char *
+get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
+{
+       const char *server_domain;
+       size_t server_domain_len;
+       size_t request_domain_len = 0;
+       unsigned long port = 0;
+       int i;
+       const char *hostbegin = NULL;
+       const char *hostend = NULL;
+       const char *portbegin;
+       char *portend;
+
+       /* DNS is case insensitive, so use case insensitive string compare here
+        */
+       server_domain = conn->ctx->config[AUTHENTICATION_DOMAIN];
+       if (!server_domain) {
+               return 0;
+       }
+       server_domain_len = strlen(server_domain);
+       if (!server_domain_len) {
+               return 0;
+       }
+
+       for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
+               if (mg_strncasecmp(uri,
+                                  abs_uri_protocols[i].proto,
+                                  abs_uri_protocols[i].proto_len) == 0) {
+
+                       hostbegin = uri + abs_uri_protocols[i].proto_len;
+                       hostend = strchr(hostbegin, '/');
+                       if (!hostend) {
+                               return 0;
+                       }
+                       portbegin = strchr(hostbegin, ':');
+                       if ((!portbegin) || (portbegin > hostend)) {
+                               port = abs_uri_protocols[i].default_port;
+                               request_domain_len = (size_t)(hostend - hostbegin);
+                       } else {
+                               port = strtoul(portbegin + 1, &portend, 10);
+                               if ((portend != hostend) || !port || !is_valid_port(port)) {
+                                       return 0;
+                               }
+                               request_domain_len = (size_t)(portbegin - hostbegin);
+                       }
+                       /* protocol found, port set */
+                       break;
+               }
+       }
+
+       if (!port) {
+               /* port remains 0 if the protocol is not found */
+               return 0;
+       }
+
+#if defined(USE_IPV6)
+       if (conn->client.lsa.sa.sa_family == AF_INET6) {
+               if (ntohs(conn->client.lsa.sin6.sin6_port) != port) {
+                       /* Request is directed to a different port */
+                       return 0;
+               }
+       } else
+#endif
+       {
+               if (ntohs(conn->client.lsa.sin.sin_port) != port) {
+                       /* Request is directed to a different port */
+                       return 0;
+               }
+       }
+
+       if ((request_domain_len != server_domain_len)
+           || (0 != memcmp(server_domain, hostbegin, server_domain_len))) {
+               /* Request is directed to another server */
+               return 0;
+       }
+
+       return hostend;
+}
+
+
+static int
+getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
+{
+       const char *cl;
+
+       if (ebuf_len > 0) {
+               ebuf[0] = '\0';
+       }
+       *err = 0;
+
+       reset_per_request_attributes(conn);
+
+       if (!conn) {
+               mg_snprintf(conn,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "Internal error");
+               *err = 500;
+               return 0;
+       }
+       /* Set the time the request was received. This value should be used for
+        * timeouts. */
+       clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
+
+       conn->request_len =
+           read_request(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
+       /* assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
+        */
+       if (conn->request_len >= 0 && conn->data_len < conn->request_len) {
+               mg_snprintf(conn,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "Invalid request size");
+               *err = 500;
+               return 0;
+       }
+
+       if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
+               mg_snprintf(conn,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "Request Too Large");
+               *err = 413;
+               return 0;
+       } else if (conn->request_len <= 0) {
+               if (conn->data_len > 0) {
+                       mg_snprintf(conn,
+                                   NULL, /* No truncation check for ebuf */
+                                   ebuf,
+                                   ebuf_len,
+                                   "%s",
+                                   "Client sent malformed request");
+                       *err = 400;
+               } else {
+                       /* Server did not send anything -> just close the connection */
+                       conn->must_close = 1;
+                       mg_snprintf(conn,
+                                   NULL, /* No truncation check for ebuf */
+                                   ebuf,
+                                   ebuf_len,
+                                   "%s",
+                                   "Client did not send a request");
+                       *err = 0;
+               }
+               return 0;
+       } else if (parse_http_message(conn->buf,
+                                     conn->buf_size,
+                                     &conn->request_info) <= 0) {
+               mg_snprintf(conn,
+                           NULL, /* No truncation check for ebuf */
+                           ebuf,
+                           ebuf_len,
+                           "%s",
+                           "Bad Request");
+               *err = 400;
+               return 0;
+       } else {
+               /* Message is a valid request or response */
+               if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
+                       /* Request/response has content length set */
+                       char *endptr = NULL;
+                       conn->content_len = strtoll(cl, &endptr, 10);
+                       if (endptr == cl) {
+                               mg_snprintf(conn,
+                                           NULL, /* No truncation check for ebuf */
+                                           ebuf,
+                                           ebuf_len,
+                                           "%s",
+                                           "Bad Request");
+                               *err = 411;
+                               return 0;
+                       }
+                       /* Publish the content length back to the request info. */
+                       conn->request_info.content_length = conn->content_len;
+               } else if ((cl = get_header(&conn->request_info, "Transfer-Encoding"))
+                              != NULL
+                          && !mg_strcasecmp(cl, "chunked")) {
+                       conn->is_chunked = 1;
+               } else if (!mg_strcasecmp(conn->request_info.request_method, "POST")
+                          || !mg_strcasecmp(conn->request_info.request_method,
+                                            "PUT")) {
+                       /* POST or PUT request without content length set */
+                       conn->content_len = -1;
+               } else if (!mg_strncasecmp(conn->request_info.request_method,
+                                          "HTTP/",
+                                          5)) {
+                       /* Response without content length set */
+                       conn->content_len = -1;
+               } else {
+                       /* Other request */
+                       conn->content_len = 0;
+               }
+       }
+       return 1;
+}
+
+
+int
+mg_get_response(struct mg_connection *conn,
+                char *ebuf,
+                size_t ebuf_len,
+                int timeout)
+{
+       if (conn) {
+               /* Implementation of API function for HTTP clients */
+               int err, ret;
+               struct mg_context *octx = conn->ctx;
+               struct mg_context rctx = *(conn->ctx);
+               char txt[32]; /* will not overflow */
+
+               if (timeout >= 0) {
+                       mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
+                       rctx.config[REQUEST_TIMEOUT] = txt;
+                       set_sock_timeout(conn->client.sock, timeout);
+               } else {
+                       rctx.config[REQUEST_TIMEOUT] = NULL;
+               }
+
+               conn->ctx = &rctx;
+               ret = getreq(conn, ebuf, ebuf_len, &err);
+               conn->ctx = octx;
+
+               /* TODO: 1) uri is deprecated;
+                *       2) here, ri.uri is the http response code */
+               conn->request_info.uri = conn->request_info.request_uri;
+
+               /* TODO (mid): Define proper return values - maybe return length?
+                * For the first test use <0 for error and >0 for OK */
+               return (ret == 0) ? -1 : +1;
+       }
+       return -1;
+}
+
+
+struct mg_connection *
+mg_download(const char *host,
+            int port,
+            int use_ssl,
+            char *ebuf,
+            size_t ebuf_len,
+            const char *fmt,
+            ...)
+{
+       struct mg_connection *conn;
+       va_list ap;
+       int i;
+       int reqerr;
+
+       va_start(ap, fmt);
+       ebuf[0] = '\0';
+
+       /* open a connection */
+       conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
+
+       if (conn != NULL) {
+               i = mg_vprintf(conn, fmt, ap);
+               if (i <= 0) {
+                       mg_snprintf(conn,
+                                   NULL, /* No truncation check for ebuf */
+                                   ebuf,
+                                   ebuf_len,
+                                   "%s",
+                                   "Error sending request");
+               } else {
+                       getreq(conn, ebuf, ebuf_len, &reqerr);
+
+                       /* TODO: 1) uri is deprecated;
+                        *       2) here, ri.uri is the http response code */
+                       conn->request_info.uri = conn->request_info.request_uri;
+               }
+       }
+
+       /* if an error occured, close the connection */
+       if (ebuf[0] != '\0' && conn != NULL) {
+               mg_close_connection(conn);
+               conn = NULL;
+       }
+
+       va_end(ap);
+       return conn;
+}
+
+
+struct websocket_client_thread_data {
+       struct mg_connection *conn;
+       mg_websocket_data_handler data_handler;
+       mg_websocket_close_handler close_handler;
+       void *callback_data;
+};
+
+
+#if defined(USE_WEBSOCKET)
+#ifdef _WIN32
+static unsigned __stdcall websocket_client_thread(void *data)
+#else
+static void *
+websocket_client_thread(void *data)
+#endif
+{
+       struct websocket_client_thread_data *cdata =
+           (struct websocket_client_thread_data *)data;
+
+       mg_set_thread_name("ws-client");
+
+       if (cdata->conn->ctx) {
+               if (cdata->conn->ctx->callbacks.init_thread) {
+                       /* 3 indicates a websocket client thread */
+                       /* TODO: check if conn->ctx can be set */
+                       cdata->conn->ctx->callbacks.init_thread(cdata->conn->ctx, 3);
+               }
+       }
+
+       read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
+
+       DEBUG_TRACE("%s", "Websocket client thread exited\n");
+
+       if (cdata->close_handler != NULL) {
+               cdata->close_handler(cdata->conn, cdata->callback_data);
+       }
+
+       mg_free((void *)cdata);
+
+#ifdef _WIN32
+       return 0;
+#else
+       return NULL;
+#endif
+}
+#endif
+
+
+struct mg_connection *
+mg_connect_websocket_client(const char *host,
+                            int port,
+                            int use_ssl,
+                            char *error_buffer,
+                            size_t error_buffer_size,
+                            const char *path,
+                            const char *origin,
+                            mg_websocket_data_handler data_func,
+                            mg_websocket_close_handler close_func,
+                            void *user_data)
+{
+       struct mg_connection *conn = NULL;
+
+#if defined(USE_WEBSOCKET)
+       struct mg_context *newctx = NULL;
+       struct websocket_client_thread_data *thread_data;
+       static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
+       static const char *handshake_req;
+
+       if (origin != NULL) {
+               handshake_req = "GET %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Upgrade: websocket\r\n"
+                               "Connection: Upgrade\r\n"
+                               "Sec-WebSocket-Key: %s\r\n"
+                               "Sec-WebSocket-Version: 13\r\n"
+                               "Origin: %s\r\n"
+                               "\r\n";
+       } else {
+               handshake_req = "GET %s HTTP/1.1\r\n"
+                               "Host: %s\r\n"
+                               "Upgrade: websocket\r\n"
+                               "Connection: Upgrade\r\n"
+                               "Sec-WebSocket-Key: %s\r\n"
+                               "Sec-WebSocket-Version: 13\r\n"
+                               "\r\n";
+       }
+
+       /* Establish the client connection and request upgrade */
+       conn = mg_download(host,
+                          port,
+                          use_ssl,
+                          error_buffer,
+                          error_buffer_size,
+                          handshake_req,
+                          path,
+                          host,
+                          magic,
+                          origin);
+
+       /* Connection object will be null if something goes wrong */
+       if (conn == NULL || (strcmp(conn->request_info.request_uri, "101") != 0)) {
+               if (!*error_buffer) {
+                       /* if there is a connection, but it did not return 101,
+                        * error_buffer is not yet set */
+                       mg_snprintf(conn,
+                                   NULL, /* No truncation check for ebuf */
+                                   error_buffer,
+                                   error_buffer_size,
+                                   "Unexpected server reply");
+               }
+               DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
+               if (conn != NULL) {
+                       mg_free(conn);
+                       conn = NULL;
+               }
+               return conn;
+       }
+
+       /* For client connections, mg_context is fake. Since we need to set a
+        * callback function, we need to create a copy and modify it. */
+       newctx = (struct mg_context *)mg_malloc(sizeof(struct mg_context));
+       memcpy(newctx, conn->ctx, sizeof(struct mg_context));
+       newctx->user_data = user_data;
+       newctx->context_type = 2;       /* client context type */
+       newctx->cfg_worker_threads = 1; /* one worker thread will be created */
+       newctx->workerthreadids =
+           (pthread_t *)mg_calloc(newctx->cfg_worker_threads, sizeof(pthread_t));
+       conn->ctx = newctx;
+       thread_data = (struct websocket_client_thread_data *)
+           mg_calloc(sizeof(struct websocket_client_thread_data), 1);
+       thread_data->conn = conn;
+       thread_data->data_handler = data_func;
+       thread_data->close_handler = close_func;
+       thread_data->callback_data = NULL;
+
+       /* Start a thread to read the websocket client connection
+        * This thread will automatically stop when mg_disconnect is
+        * called on the client connection */
+       if (mg_start_thread_with_id(websocket_client_thread,
+                                   (void *)thread_data,
+                                   newctx->workerthreadids) != 0) {
+               mg_free((void *)thread_data);
+               mg_free((void *)newctx->workerthreadids);
+               mg_free((void *)newctx);
+               mg_free((void *)conn);
+               conn = NULL;
+               DEBUG_TRACE("%s",
+                           "Websocket client connect thread could not be started\r\n");
+       }
+#else
+       /* Appease "unused parameter" warnings */
+       (void)host;
+       (void)port;
+       (void)use_ssl;
+       (void)error_buffer;
+       (void)error_buffer_size;
+       (void)path;
+       (void)origin;
+       (void)user_data;
+       (void)data_func;
+       (void)close_func;
+#endif
+
+       return conn;
+}
+
+
+static void
+process_new_connection(struct mg_connection *conn)
+{
+       if (conn && conn->ctx) {
+               struct mg_request_info *ri = &conn->request_info;
+               int keep_alive_enabled, keep_alive, discard_len;
+               char ebuf[100];
+               const char *hostend;
+               int reqerr, uri_type;
+
+               keep_alive_enabled =
+                   !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
+
+               /* Important: on new connection, reset the receiving buffer. Credit
+                * goes to crule42. */
+               conn->data_len = 0;
+               do {
+                       if (!getreq(conn, ebuf, sizeof(ebuf), &reqerr)) {
+                               /* The request sent by the client could not be understood by
+                                * the server, or it was incomplete or a timeout. Send an
+                                * error message and close the connection. */
+                               if (reqerr > 0) {
+                                       /*assert(ebuf[0] != '\0');*/
+                                       send_http_error(conn, reqerr, "%s", ebuf);
+                               }
+                       } else if (strcmp(ri->http_version, "1.0")
+                                  && strcmp(ri->http_version, "1.1")) {
+                               mg_snprintf(conn,
+                                           NULL, /* No truncation check for ebuf */
+                                           ebuf,
+                                           sizeof(ebuf),
+                                           "Bad HTTP version: [%s]",
+                                           ri->http_version);
+                               send_http_error(conn, 505, "%s", ebuf);
+                       }
+
+                       if (ebuf[0] == '\0') {
+                               uri_type = get_uri_type(conn->request_info.request_uri);
+                               switch (uri_type) {
+                               case 1:
+                                       /* Asterisk */
+                                       conn->request_info.local_uri = NULL;
+                                       break;
+                               case 2:
+                                       /* relative uri */
+                                       conn->request_info.local_uri =
+                                           conn->request_info.request_uri;
+                                       break;
+                               case 3:
+                               case 4:
+                                       /* absolute uri (with/without port) */
+                                       hostend = get_rel_url_at_current_server(
+                                           conn->request_info.request_uri, conn);
+                                       if (hostend) {
+                                               conn->request_info.local_uri = hostend;
+                                       } else {
+                                               conn->request_info.local_uri = NULL;
+                                       }
+                                       break;
+                               default:
+                                       mg_snprintf(conn,
+                                                   NULL, /* No truncation check for ebuf */
+                                                   ebuf,
+                                                   sizeof(ebuf),
+                                                   "Invalid URI: [%s]",
+                                                   ri->request_uri);
+                                       send_http_error(conn, 400, "%s", ebuf);
+                                       break;
+                               }
+
+                               /* TODO: cleanup uri, local_uri and request_uri */
+                               conn->request_info.uri = conn->request_info.local_uri;
+                       }
+
+                       if (ebuf[0] == '\0') {
+                               if (conn->request_info.local_uri) {
+                                       /* handle request to local server */
+                                       handle_request(conn);
+                                       if (conn->ctx->callbacks.end_request != NULL) {
+                                               conn->ctx->callbacks.end_request(conn,
+                                                                                conn->status_code);
+                                       }
+                                       log_access(conn);
+                               } else {
+                                       /* TODO: handle non-local request (PROXY) */
+                                       conn->must_close = 1;
+                               }
+                       } else {
+                               conn->must_close = 1;
+                       }
+
+                       if (ri->remote_user != NULL) {
+                               mg_free((void *)ri->remote_user);
+                               /* Important! When having connections with and without auth
+                                * would cause double free and then crash */
+                               ri->remote_user = NULL;
+                       }
+
+                       /* NOTE(lsm): order is important here. should_keep_alive() call
+                        * is
+                        * using parsed request, which will be invalid after memmove's
+                        * below.
+                        * Therefore, memorize should_keep_alive() result now for later
+                        * use
+                        * in loop exit condition. */
+                       keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled
+                                    && conn->content_len >= 0 && should_keep_alive(conn);
+
+                       /* Discard all buffered data for this request */
+                       discard_len = conn->content_len >= 0 && conn->request_len > 0
+                                             && conn->request_len + conn->content_len
+                                                    < (int64_t)conn->data_len
+                                         ? (int)(conn->request_len + conn->content_len)
+                                         : conn->data_len;
+                       /*assert(discard_len >= 0);*/
+                       if (discard_len < 0)
+                               break;
+                       conn->data_len -= discard_len;
+                       if (conn->data_len > 0) {
+                               memmove(conn->buf,
+                                       conn->buf + discard_len,
+                                       (size_t)conn->data_len);
+                       }
+
+                       /* assert(conn->data_len >= 0); */
+                       /* assert(conn->data_len <= conn->buf_size); */
+
+                       if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
+                               break;
+                       }
+
+               } while (keep_alive);
+       }
+}
+
+
+/* Worker threads take accepted socket from the queue */
+static int
+consume_socket(struct mg_context *ctx, struct socket *sp)
+{
+#define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
+       if (!ctx) {
+               return 0;
+       }
+
+       (void)pthread_mutex_lock(&ctx->thread_mutex);
+       DEBUG_TRACE("%s", "going idle");
+
+       /* If the queue is empty, wait. We're idle at this point. */
+       while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
+               pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
+       }
+
+       /* If we're stopping, sq_head may be equal to sq_tail. */
+       if (ctx->sq_head > ctx->sq_tail) {
+               /* Copy socket from the queue and increment tail */
+               *sp = ctx->queue[ctx->sq_tail % QUEUE_SIZE(ctx)];
+               ctx->sq_tail++;
+
+               DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
+
+               /* Wrap pointers if needed */
+               while (ctx->sq_tail > QUEUE_SIZE(ctx)) {
+                       ctx->sq_tail -= QUEUE_SIZE(ctx);
+                       ctx->sq_head -= QUEUE_SIZE(ctx);
+               }
+       }
+
+       (void)pthread_cond_signal(&ctx->sq_empty);
+       (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+       return !ctx->stop_flag;
+#undef QUEUE_SIZE
+}
+
+
+static void *
+worker_thread_run(void *thread_func_param)
+{
+       struct mg_context *ctx = (struct mg_context *)thread_func_param;
+       struct mg_connection *conn;
+       struct mg_workerTLS tls;
+#if defined(MG_LEGACY_INTERFACE)
+       uint32_t addr;
+#endif
+
+       mg_set_thread_name("worker");
+
+       tls.is_master = 0;
+       tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+       if (ctx->callbacks.init_thread) {
+               /* call init_thread for a worker thread (type 1) */
+               ctx->callbacks.init_thread(ctx, 1);
+       }
+
+       conn =
+           (struct mg_connection *)mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
+       if (conn == NULL) {
+               mg_cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
+       } else {
+               pthread_setspecific(sTlsKey, &tls);
+               conn->buf_size = MAX_REQUEST_SIZE;
+               conn->buf = (char *)(conn + 1);
+               conn->ctx = ctx;
+               conn->request_info.user_data = ctx->user_data;
+               /* Allocate a mutex for this connection to allow communication both
+                * within the request handler and from elsewhere in the application
+                */
+               (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
+
+               /* Call consume_socket() even when ctx->stop_flag > 0, to let it
+                * signal sq_empty condvar to wake up the master waiting in
+                * produce_socket() */
+               while (consume_socket(ctx, &conn->client)) {
+                       conn->conn_birth_time = time(NULL);
+
+/* Fill in IP, port info early so even if SSL setup below fails,
+ * error handler would have the corresponding info.
+ * Thanks to Johannes Winkelmann for the patch.
+ */
+#if defined(USE_IPV6)
+                       if (conn->client.rsa.sa.sa_family == AF_INET6) {
+                               conn->request_info.remote_port =
+                                   ntohs(conn->client.rsa.sin6.sin6_port);
+                       } else
+#endif
+                       {
+                               conn->request_info.remote_port =
+                                   ntohs(conn->client.rsa.sin.sin_port);
+                       }
+
+                       sockaddr_to_string(conn->request_info.remote_addr,
+                                          sizeof(conn->request_info.remote_addr),
+                                          &conn->client.rsa);
+
+#if defined(MG_LEGACY_INTERFACE)
+                       /* This legacy interface only works for the IPv4 case */
+                       addr = ntohl(conn->client.rsa.sin.sin_addr.s_addr);
+                       memcpy(&conn->request_info.remote_ip, &addr, 4);
+#endif
+
+                       conn->request_info.is_ssl = conn->client.is_ssl;
+
+                       if (!conn->client.is_ssl
+#ifndef NO_SSL
+                           || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
+#endif
+                               ) {
+
+
+                               process_new_connection(conn);
+                       }
+
+                       close_connection(conn);
+               }
+       }
+
+       /* Signal master that we're done with connection and exiting */
+       (void)pthread_mutex_lock(&ctx->thread_mutex);
+       ctx->running_worker_threads--;
+       (void)pthread_cond_signal(&ctx->thread_cond);
+       /* assert(ctx->running_worker_threads >= 0); */
+       (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+       pthread_setspecific(sTlsKey, NULL);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       CloseHandle(tls.pthread_cond_helper_mutex);
+#endif
+       pthread_mutex_destroy(&conn->mutex);
+       mg_free(conn);
+
+       DEBUG_TRACE("%s", "exiting");
+       return NULL;
+}
+
+
+/* Threads have different return types on Windows and Unix. */
+#ifdef _WIN32
+static unsigned __stdcall worker_thread(void *thread_func_param)
+{
+       worker_thread_run(thread_func_param);
+       return 0;
+}
+#else
+static void *
+worker_thread(void *thread_func_param)
+{
+       worker_thread_run(thread_func_param);
+       return NULL;
+}
+#endif /* _WIN32 */
+
+
+/* Master thread adds accepted socket to a queue */
+static void
+produce_socket(struct mg_context *ctx, const struct socket *sp)
+{
+#define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
+       if (!ctx) {
+               return;
+       }
+       (void)pthread_mutex_lock(&ctx->thread_mutex);
+
+       /* If the queue is full, wait */
+       while (ctx->stop_flag == 0
+              && ctx->sq_head - ctx->sq_tail >= QUEUE_SIZE(ctx)) {
+               (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
+       }
+
+       if (ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx)) {
+               /* Copy socket to the queue and increment head */
+               ctx->queue[ctx->sq_head % QUEUE_SIZE(ctx)] = *sp;
+               ctx->sq_head++;
+               DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
+       }
+
+       (void)pthread_cond_signal(&ctx->sq_full);
+       (void)pthread_mutex_unlock(&ctx->thread_mutex);
+#undef QUEUE_SIZE
+}
+
+
+static void
+accept_new_connection(const struct socket *listener, struct mg_context *ctx)
+{
+       struct socket so;
+       char src_addr[IP_ADDR_STR_LEN];
+       socklen_t len = sizeof(so.rsa);
+       int on = 1;
+       int timeout;
+
+       if (!listener) {
+               return;
+       }
+
+       if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
+           == INVALID_SOCKET) {
+       } else if (!check_acl(ctx, ntohl(*(uint32_t *)&so.rsa.sin.sin_addr))) {
+               sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
+               mg_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
+               closesocket(so.sock);
+               so.sock = INVALID_SOCKET;
+       } else {
+               /* Put so socket structure into the queue */
+               DEBUG_TRACE("Accepted socket %d", (int)so.sock);
+               set_close_on_exec(so.sock, fc(ctx));
+               so.is_ssl = listener->is_ssl;
+               so.ssl_redir = listener->ssl_redir;
+               if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
+                       mg_cry(fc(ctx),
+                              "%s: getsockname() failed: %s",
+                              __func__,
+                              strerror(ERRNO));
+               }
+
+               /* Set TCP keep-alive. This is needed because if HTTP-level
+                * keep-alive
+                * is enabled, and client resets the connection, server won't get
+                * TCP FIN or RST and will keep the connection open forever. With
+                * TCP keep-alive, next keep-alive handshake will figure out that
+                * the client is down and will close the server end.
+                * Thanks to Igor Klopov who suggested the patch. */
+               if (setsockopt(so.sock,
+                              SOL_SOCKET,
+                              SO_KEEPALIVE,
+                              (SOCK_OPT_TYPE)&on,
+                              sizeof(on)) != 0) {
+                       mg_cry(fc(ctx),
+                              "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
+                              __func__,
+                              strerror(ERRNO));
+               }
+
+
+               /* Disable TCP Nagle's algorithm.  Normally TCP packets are
+                * coalesced
+                * to effectively fill up the underlying IP packet payload and
+                * reduce
+                * the overhead of sending lots of small buffers. However this hurts
+                * the server's throughput (ie. operations per second) when HTTP 1.1
+                * persistent connections are used and the responses are relatively
+                * small (eg. less than 1400 bytes).
+                */
+               if ((ctx != NULL) && (ctx->config[CONFIG_TCP_NODELAY] != NULL)
+                   && (!strcmp(ctx->config[CONFIG_TCP_NODELAY], "1"))) {
+                       if (set_tcp_nodelay(so.sock, 1) != 0) {
+                               mg_cry(fc(ctx),
+                                      "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
+                                      __func__,
+                                      strerror(ERRNO));
+                       }
+               }
+
+               if (ctx && ctx->config[REQUEST_TIMEOUT]) {
+                       timeout = atoi(ctx->config[REQUEST_TIMEOUT]);
+               } else {
+                       timeout = -1;
+               }
+
+               /* Set socket timeout to the given value, but not more than a
+                * a certain limit (SOCKET_TIMEOUT_QUANTUM, default 10 seconds),
+                * so the server can exit after that time if requested. */
+               if ((timeout > 0) && (timeout < SOCKET_TIMEOUT_QUANTUM)) {
+                       set_sock_timeout(so.sock, timeout);
+               } else {
+                       set_sock_timeout(so.sock, SOCKET_TIMEOUT_QUANTUM);
+               }
+
+               produce_socket(ctx, &so);
+       }
+}
+
+
+static void
+master_thread_run(void *thread_func_param)
+{
+       struct mg_context *ctx = (struct mg_context *)thread_func_param;
+       struct mg_workerTLS tls;
+       struct pollfd *pfd;
+       unsigned int i;
+       unsigned int workerthreadcount;
+
+       if (!ctx) {
+               return;
+       }
+
+       mg_set_thread_name("master");
+
+/* Increase priority of the master thread */
+#if defined(_WIN32)
+       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+#elif defined(USE_MASTER_THREAD_PRIORITY)
+       int min_prio = sched_get_priority_min(SCHED_RR);
+       int max_prio = sched_get_priority_max(SCHED_RR);
+       if ((min_prio >= 0) && (max_prio >= 0)
+           && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
+           && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
+               struct sched_param sched_param = {0};
+               sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
+               pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
+       }
+#endif
+
+/* Initialize thread local storage */
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+       tls.is_master = 1;
+       pthread_setspecific(sTlsKey, &tls);
+
+       if (ctx->callbacks.init_thread) {
+               /* Callback for the master thread (type 0) */
+               ctx->callbacks.init_thread(ctx, 0);
+       }
+
+       /* Server starts *now* */
+       ctx->start_time = time(NULL);
+
+       /* Allocate memory for the listening sockets, and start the server */
+       pfd =
+           (struct pollfd *)mg_calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
+       while (pfd != NULL && ctx->stop_flag == 0) {
+               for (i = 0; i < ctx->num_listening_sockets; i++) {
+                       pfd[i].fd = ctx->listening_sockets[i].sock;
+                       pfd[i].events = POLLIN;
+               }
+
+               if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
+                       for (i = 0; i < ctx->num_listening_sockets; i++) {
+                               /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
+                                * successful poll, and POLLIN is defined as
+                                * (POLLRDNORM | POLLRDBAND)
+                                * Therefore, we're checking pfd[i].revents & POLLIN, not
+                                * pfd[i].revents == POLLIN. */
+                               if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
+                                       accept_new_connection(&ctx->listening_sockets[i], ctx);
+                               }
+                       }
+               }
+       }
+       mg_free(pfd);
+       DEBUG_TRACE("%s", "stopping workers");
+
+       /* Stop signal received: somebody called mg_stop. Quit. */
+       close_all_listening_sockets(ctx);
+
+       /* Wakeup workers that are waiting for connections to handle. */
+       pthread_cond_broadcast(&ctx->sq_full);
+
+       /* Wait until all threads finish */
+       (void)pthread_mutex_lock(&ctx->thread_mutex);
+       while (ctx->running_worker_threads > 0) {
+               (void)pthread_cond_wait(&ctx->thread_cond, &ctx->thread_mutex);
+       }
+       (void)pthread_mutex_unlock(&ctx->thread_mutex);
+
+       /* Join all worker threads to avoid leaking threads. */
+       workerthreadcount = ctx->cfg_worker_threads;
+       for (i = 0; i < workerthreadcount; i++) {
+               if (ctx->workerthreadids[i] != 0) {
+                       mg_join_thread(ctx->workerthreadids[i]);
+               }
+       }
+
+#if !defined(NO_SSL)
+       if (ctx->ssl_ctx != NULL) {
+               uninitialize_ssl(ctx);
+       }
+#endif
+       DEBUG_TRACE("%s", "exiting");
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       CloseHandle(tls.pthread_cond_helper_mutex);
+#endif
+       pthread_setspecific(sTlsKey, NULL);
+
+       /* Signal mg_stop() that we're done.
+        * WARNING: This must be the very last thing this
+        * thread does, as ctx becomes invalid after this line. */
+       ctx->stop_flag = 2;
+}
+
+
+/* Threads have different return types on Windows and Unix. */
+#ifdef _WIN32
+static unsigned __stdcall master_thread(void *thread_func_param)
+{
+       master_thread_run(thread_func_param);
+       return 0;
+}
+#else
+static void *
+master_thread(void *thread_func_param)
+{
+       master_thread_run(thread_func_param);
+       return NULL;
+}
+#endif /* _WIN32 */
+
+
+static void
+free_context(struct mg_context *ctx)
+{
+       int i;
+       struct mg_handler_info *tmp_rh;
+
+       if (ctx == NULL) {
+               return;
+       }
+
+       if (ctx->callbacks.exit_context) {
+               ctx->callbacks.exit_context(ctx);
+       }
+
+       /* All threads exited, no sync is needed. Destroy thread mutex and
+        * condvars
+        */
+       (void)pthread_mutex_destroy(&ctx->thread_mutex);
+       (void)pthread_cond_destroy(&ctx->thread_cond);
+       (void)pthread_cond_destroy(&ctx->sq_empty);
+       (void)pthread_cond_destroy(&ctx->sq_full);
+
+       /* Destroy other context global data structures mutex */
+       (void)pthread_mutex_destroy(&ctx->nonce_mutex);
+
+#if defined(USE_TIMERS)
+       timers_exit(ctx);
+#endif
+
+       /* Deallocate config parameters */
+       for (i = 0; i < NUM_OPTIONS; i++) {
+               if (ctx->config[i] != NULL) {
+#if defined(_MSC_VER)
+#pragma warning(suppress : 6001)
+#endif
+                       mg_free(ctx->config[i]);
+               }
+       }
+
+       /* Deallocate request handlers */
+       while (ctx->handlers) {
+               tmp_rh = ctx->handlers;
+               ctx->handlers = tmp_rh->next;
+               mg_free(tmp_rh->uri);
+               mg_free(tmp_rh);
+       }
+
+#ifndef NO_SSL
+       /* Deallocate SSL context */
+       if (ctx->ssl_ctx != NULL) {
+               SSL_CTX_free(ctx->ssl_ctx);
+       }
+#endif /* !NO_SSL */
+
+       /* Deallocate worker thread ID array */
+       if (ctx->workerthreadids != NULL) {
+               mg_free(ctx->workerthreadids);
+       }
+
+       /* Deallocate the tls variable */
+       if (mg_atomic_dec(&sTlsInit) == 0) {
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+               DeleteCriticalSection(&global_log_file_lock);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+#if !defined(_WIN32)
+               pthread_mutexattr_destroy(&pthread_mutex_attr);
+#endif
+
+               pthread_key_delete(sTlsKey);
+       }
+
+       /* deallocate system name string */
+       mg_free(ctx->systemName);
+
+       /* Deallocate context itself */
+       mg_free(ctx);
+}
+
+
+void
+mg_stop(struct mg_context *ctx)
+{
+       pthread_t mt;
+       if (!ctx) {
+               return;
+       }
+
+       /* We don't use a lock here. Calling mg_stop with the same ctx from
+        * two threads is not allowed. */
+       mt = ctx->masterthreadid;
+       if (mt == 0) {
+               return;
+       }
+
+       ctx->masterthreadid = 0;
+       ctx->stop_flag = 1;
+
+       /* Wait until mg_fini() stops */
+       while (ctx->stop_flag != 2) {
+               (void)mg_sleep(10);
+       }
+
+       mg_join_thread(mt);
+       free_context(ctx);
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       (void)WSACleanup();
+#endif /* _WIN32 && !__SYMBIAN32__ */
+}
+
+
+static void
+get_system_name(char **sysName)
+{
+#if defined(_WIN32)
+#if !defined(__SYMBIAN32__)
+       char name[128];
+       DWORD dwVersion = 0;
+       DWORD dwMajorVersion = 0;
+       DWORD dwMinorVersion = 0;
+       DWORD dwBuild = 0;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+// GetVersion was declared deprecated
+#pragma warning(disable : 4996)
+#endif
+       dwVersion = GetVersion();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+       dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
+       dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
+       dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
+       (void)dwBuild;
+
+       sprintf(name,
+               "Windows %u.%u",
+               (unsigned)dwMajorVersion,
+               (unsigned)dwMinorVersion);
+       *sysName = mg_strdup(name);
+#else
+       *sysName = mg_strdup("Symbian");
+#endif
+#else
+       struct utsname name;
+       memset(&name, 0, sizeof(name));
+       uname(&name);
+       *sysName = mg_strdup(name.sysname);
+#endif
+}
+
+
+struct mg_context *
+mg_start(const struct mg_callbacks *callbacks,
+         void *user_data,
+         const char **options)
+{
+       struct mg_context *ctx;
+       const char *name, *value, *default_value;
+       int idx, ok, workerthreadcount;
+       unsigned int i;
+       void (*exit_callback)(const struct mg_context *ctx) = 0;
+
+       struct mg_workerTLS tls;
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       WSADATA data;
+       WSAStartup(MAKEWORD(2, 2), &data);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+
+       /* Allocate context and initialize reasonable general case defaults. */
+       if ((ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx))) == NULL) {
+               return NULL;
+       }
+
+       /* Random number generator will initialize at the first call */
+       ctx->auth_nonce_mask =
+           (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
+
+       if (mg_atomic_inc(&sTlsInit) == 1) {
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+               InitializeCriticalSection(&global_log_file_lock);
+#endif /* _WIN32 && !__SYMBIAN32__ */
+#if !defined(_WIN32)
+               pthread_mutexattr_init(&pthread_mutex_attr);
+               pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+#endif
+
+               if (0 != pthread_key_create(&sTlsKey, tls_dtor)) {
+                       /* Fatal error - abort start. However, this situation should
+                        * never
+                        * occur in practice. */
+                       mg_atomic_dec(&sTlsInit);
+                       mg_cry(fc(ctx), "Cannot initialize thread local storage");
+                       mg_free(ctx);
+                       return NULL;
+               }
+       } else {
+               /* TODO (low): istead of sleeping, check if sTlsKey is already
+                * initialized. */
+               mg_sleep(1);
+       }
+
+       tls.is_master = -1;
+       tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       tls.pthread_cond_helper_mutex = NULL;
+#endif
+       pthread_setspecific(sTlsKey, &tls);
+
+#if defined(USE_LUA)
+       lua_init_optional_libraries();
+#endif
+
+       ok = 0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr);
+       ok &= 0 == pthread_cond_init(&ctx->thread_cond, NULL);
+       ok &= 0 == pthread_cond_init(&ctx->sq_empty, NULL);
+       ok &= 0 == pthread_cond_init(&ctx->sq_full, NULL);
+       ok &= 0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr);
+       if (!ok) {
+               /* Fatal error - abort start. However, this situation should never
+                * occur in practice. */
+               mg_cry(fc(ctx), "Cannot initialize thread synchronization objects");
+               mg_free(ctx);
+               pthread_setspecific(sTlsKey, NULL);
+               return NULL;
+       }
+
+       if (callbacks) {
+               ctx->callbacks = *callbacks;
+               exit_callback = callbacks->exit_context;
+               ctx->callbacks.exit_context = 0;
+       }
+       ctx->user_data = user_data;
+       ctx->handlers = NULL;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       ctx->shared_lua_websockets = 0;
+#endif
+
+       while (options && (name = *options++) != NULL) {
+               if ((idx = get_option_index(name)) == -1) {
+                       mg_cry(fc(ctx), "Invalid option: %s", name);
+                       free_context(ctx);
+                       pthread_setspecific(sTlsKey, NULL);
+                       return NULL;
+               } else if ((value = *options++) == NULL) {
+                       mg_cry(fc(ctx), "%s: option value cannot be NULL", name);
+                       free_context(ctx);
+                       pthread_setspecific(sTlsKey, NULL);
+                       return NULL;
+               }
+               if (ctx->config[idx] != NULL) {
+                       mg_cry(fc(ctx), "warning: %s: duplicate option", name);
+                       mg_free(ctx->config[idx]);
+               }
+               ctx->config[idx] = mg_strdup(value);
+               DEBUG_TRACE("[%s] -> [%s]", name, value);
+       }
+
+       /* Set default value if needed */
+       for (i = 0; config_options[i].name != NULL; i++) {
+               default_value = config_options[i].default_value;
+               if (ctx->config[i] == NULL && default_value != NULL) {
+                       ctx->config[i] = mg_strdup(default_value);
+               }
+       }
+
+#if defined(NO_FILES)
+       if (ctx->config[DOCUMENT_ROOT] != NULL) {
+               mg_cry(fc(ctx), "%s", "Document root must not be set");
+               free_context(ctx);
+               pthread_setspecific(sTlsKey, NULL);
+               return NULL;
+       }
+#endif
+
+       get_system_name(&ctx->systemName);
+
+       /* NOTE(lsm): order is important here. SSL certificates must
+        * be initialized before listening ports. UID must be set last. */
+       if (!set_gpass_option(ctx) ||
+#if !defined(NO_SSL)
+           !set_ssl_option(ctx) ||
+#endif
+           !set_ports_option(ctx) ||
+#if !defined(_WIN32)
+           !set_uid_option(ctx) ||
+#endif
+           !set_acl_option(ctx)) {
+               free_context(ctx);
+               pthread_setspecific(sTlsKey, NULL);
+               return NULL;
+       }
+
+#if !defined(_WIN32) && !defined(__SYMBIAN32__)
+       /* Ignore SIGPIPE signal, so if browser cancels the request, it
+        * won't kill the whole process. */
+       (void)signal(SIGPIPE, SIG_IGN);
+#endif /* !_WIN32 && !__SYMBIAN32__ */
+
+       workerthreadcount = atoi(ctx->config[NUM_THREADS]);
+
+       if (workerthreadcount > MAX_WORKER_THREADS) {
+               mg_cry(fc(ctx), "Too many worker threads");
+               free_context(ctx);
+               pthread_setspecific(sTlsKey, NULL);
+               return NULL;
+       }
+
+       if (workerthreadcount > 0) {
+               ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
+               ctx->workerthreadids =
+                   (pthread_t *)mg_calloc(ctx->cfg_worker_threads, sizeof(pthread_t));
+               if (ctx->workerthreadids == NULL) {
+                       mg_cry(fc(ctx), "Not enough memory for worker thread ID array");
+                       free_context(ctx);
+                       pthread_setspecific(sTlsKey, NULL);
+                       return NULL;
+               }
+       }
+
+#if defined(USE_TIMERS)
+       if (timers_init(ctx) != 0) {
+               mg_cry(fc(ctx), "Error creating timers");
+               free_context(ctx);
+               pthread_setspecific(sTlsKey, NULL);
+               return NULL;
+       }
+#endif
+
+       /* Context has been created - init user libraries */
+       if (ctx->callbacks.init_context) {
+               ctx->callbacks.init_context(ctx);
+       }
+       ctx->callbacks.exit_context = exit_callback;
+       ctx->context_type = 1; /* server context */
+
+       /* Start master (listening) thread */
+       mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
+
+       /* Start worker threads */
+       for (i = 0; i < ctx->cfg_worker_threads; i++) {
+               (void)pthread_mutex_lock(&ctx->thread_mutex);
+               ctx->running_worker_threads++;
+               (void)pthread_mutex_unlock(&ctx->thread_mutex);
+               if (mg_start_thread_with_id(worker_thread,
+                                           ctx,
+                                           &ctx->workerthreadids[i]) != 0) {
+                       (void)pthread_mutex_lock(&ctx->thread_mutex);
+                       ctx->running_worker_threads--;
+                       (void)pthread_mutex_unlock(&ctx->thread_mutex);
+                       if (i > 0) {
+                               mg_cry(fc(ctx),
+                                      "Cannot start worker thread %i: error %ld",
+                                      i + 1,
+                                      (long)ERRNO);
+                       } else {
+                               mg_cry(fc(ctx),
+                                      "Cannot create threads: error %ld",
+                                      (long)ERRNO);
+                               free_context(ctx);
+                               pthread_setspecific(sTlsKey, NULL);
+                               return NULL;
+                       }
+                       break;
+               }
+       }
+
+       pthread_setspecific(sTlsKey, NULL);
+       return ctx;
+}
+
+
+/* Feature check API function */
+unsigned
+mg_check_feature(unsigned feature)
+{
+       static const unsigned feature_set = 0
+/* Set bits for available features according to API documentation.
+ * This bit mask is created at compile time, according to the active
+ * preprocessor defines. It is a single const value at runtime. */
+#if !defined(NO_FILES)
+                                           | 0x0001u
+#endif
+#if !defined(NO_SSL)
+                                           | 0x0002u
+#endif
+#if !defined(NO_CGI)
+                                           | 0x0004u
+#endif
+#if defined(USE_IPV6)
+                                           | 0x0008u
+#endif
+#if defined(USE_WEBSOCKET)
+                                           | 0x0010u
+#endif
+#if defined(USE_LUA)
+                                           | 0x0020u
+#endif
+#if defined(USE_DUKTAPE)
+                                           | 0x0040u
+#endif
+#if !defined(NO_CACHING)
+                                           | 0x0080u
+#endif
+
+/* Set some extra bits not defined in the API documentation.
+ * These bits may change without further notice. */
+#if defined(MG_LEGACY_INTERFACE)
+                                           | 0x8000u
+#endif
+#if defined(MEMORY_DEBUGGING)
+                                           | 0x0100u
+#endif
+#if defined(USE_TIMERS)
+                                           | 0x0200u
+#endif
+#if !defined(NO_NONCE_CHECK)
+                                           | 0x0400u
+#endif
+#if !defined(NO_POPEN)
+                                           | 0x0800u
+#endif
+           ;
+       return (feature & feature_set);
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.h b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.h
new file mode 100644 (file)
index 0000000..9702985
--- /dev/null
@@ -0,0 +1,1024 @@
+/* Copyright (c) 2013-2016 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * 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.
+ */
+
+#ifndef CIVETWEB_HEADER_INCLUDED
+#define CIVETWEB_HEADER_INCLUDED
+
+#define CIVETWEB_VERSION "1.8"
+
+#ifndef CIVETWEB_API
+#if defined(_WIN32)
+#if defined(CIVETWEB_DLL_EXPORTS)
+#define CIVETWEB_API __declspec(dllexport)
+#elif defined(CIVETWEB_DLL_IMPORTS)
+#define CIVETWEB_API __declspec(dllimport)
+#else
+#define CIVETWEB_API
+#endif
+#elif __GNUC__ >= 4
+#define CIVETWEB_API __attribute__((visibility("default")))
+#else
+#define CIVETWEB_API
+#endif
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+struct mg_context;    /* Handle for the HTTP service itself */
+struct mg_connection; /* Handle for the individual connection */
+
+
+/* This structure contains information about the HTTP request. */
+struct mg_request_info {
+       const char *request_method; /* "GET", "POST", etc */
+       const char *request_uri;    /* URL-decoded URI (absolute or relative,
+                                    * as in the request) */
+       const char *local_uri;      /* URL-decoded URI (relative). Can be NULL
+                                    * if the request_uri does not address a
+                                    * resource at the server host. */
+       const char *uri;            /* Deprecated: use local_uri instead */
+       const char *http_version;   /* E.g. "1.0", "1.1" */
+       const char *query_string;   /* URL part after '?', not including '?', or
+                                      NULL */
+       const char *remote_user;    /* Authenticated user, or NULL if no auth
+                                      used */
+       char remote_addr[48];       /* Client's IP address as a string. */
+
+#if defined(MG_LEGACY_INTERFACE)
+       long remote_ip; /* Client's IP address. Deprecated: use remote_addr instead
+                          */
+#endif
+
+       long long content_length; /* Length (in bytes) of the request body,
+                                    can be -1 if no length was given. */
+       int remote_port;          /* Client's port */
+       int is_ssl;               /* 1 if SSL-ed, 0 if not */
+       void *user_data;          /* User data pointer passed to mg_start() */
+       void *conn_data;          /* Connection-specific user data */
+
+       int num_headers; /* Number of HTTP headers */
+       struct mg_header {
+               const char *name;  /* HTTP header name */
+               const char *value; /* HTTP header value */
+       } http_headers[64];    /* Maximum 64 headers */
+};
+
+
+/* This structure needs to be passed to mg_start(), to let civetweb know
+   which callbacks to invoke. For a detailed description, see
+   https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */
+struct mg_callbacks {
+       /* Called when civetweb has received new HTTP request.
+          If the callback returns one, it must process the request
+          by sending valid HTTP headers and a body. Civetweb will not do
+          any further processing. Otherwise it must return zero.
+          Note that since V1.7 the "begin_request" function is called
+          before an authorization check. If an authorization check is
+          required, use a request_handler instead.
+          Return value:
+            0: civetweb will process the request itself. In this case,
+               the callback must not send any data to the client.
+            1-999: callback already processed the request. Civetweb will
+                   not send any data after the callback returned. The
+                   return code is stored as a HTTP status code for the
+                   access log. */
+       int (*begin_request)(struct mg_connection *);
+
+       /* Called when civetweb has finished processing request. */
+       void (*end_request)(const struct mg_connection *, int reply_status_code);
+
+       /* Called when civetweb is about to log a message. If callback returns
+          non-zero, civetweb does not log anything. */
+       int (*log_message)(const struct mg_connection *, const char *message);
+
+       /* Called when civetweb is about to log access. If callback returns
+          non-zero, civetweb does not log anything. */
+       int (*log_access)(const struct mg_connection *, const char *message);
+
+       /* Called when civetweb initializes SSL library.
+          Parameters:
+            user_data: parameter user_data passed when starting the server.
+          Return value:
+            0: civetweb will set up the SSL certificate.
+            1: civetweb assumes the callback already set up the certificate.
+           -1: initializing ssl fails. */
+       int (*init_ssl)(void *ssl_context, void *user_data);
+
+#if defined(MG_LEGACY_INTERFACE)
+       /* Called when websocket request is received, before websocket handshake.
+          Return value:
+            0: civetweb proceeds with websocket handshake.
+            1: connection is closed immediately.
+          This callback is deprecated: Use mg_set_websocket_handler instead. */
+       int (*websocket_connect)(const struct mg_connection *);
+
+       /* Called when websocket handshake is successfully completed, and
+          connection is ready for data exchange.
+          This callback is deprecated: Use mg_set_websocket_handler instead. */
+       void (*websocket_ready)(struct mg_connection *);
+
+       /* Called when data frame has been received from the client.
+          Parameters:
+            bits: first byte of the websocket frame, see websocket RFC at
+                  http://tools.ietf.org/html/rfc6455, section 5.2
+            data, data_len: payload, with mask (if any) already applied.
+          Return value:
+            1: keep this websocket connection open.
+            0: close this websocket connection.
+          This callback is deprecated: Use mg_set_websocket_handler instead. */
+       int (*websocket_data)(struct mg_connection *,
+                             int bits,
+                             char *data,
+                             size_t data_len);
+#endif /* MG_LEGACY_INTERFACE */
+
+       /* Called when civetweb is closing a connection.  The per-context mutex is
+          locked when this is invoked.  This is primarily useful for noting when
+          a websocket is closing and removing it from any application-maintained
+          list of clients.
+          Using this callback for websocket connections is deprecated: Use
+          mg_set_websocket_handler instead. */
+       void (*connection_close)(const struct mg_connection *);
+
+       /* Called when civetweb tries to open a file. Used to intercept file open
+          calls, and serve file data from memory instead.
+          Parameters:
+             path:     Full path to the file to open.
+             data_len: Placeholder for the file size, if file is served from
+                       memory.
+          Return value:
+            NULL: do not serve file from memory, proceed with normal file open.
+            non-NULL: pointer to the file contents in memory. data_len must be
+              initilized with the size of the memory block. */
+       const char *(*open_file)(const struct mg_connection *,
+                                const char *path,
+                                size_t *data_len);
+
+       /* Called when civetweb is about to serve Lua server page, if
+          Lua support is enabled.
+          Parameters:
+            lua_context: "lua_State *" pointer. */
+       void (*init_lua)(const struct mg_connection *, void *lua_context);
+
+#if defined(MG_LEGACY_INTERFACE)
+       /* Called when civetweb has uploaded a file to a temporary directory as a
+          result of mg_upload() call.
+          Note that mg_upload is deprecated. Use mg_handle_form_request instead.
+          Parameters:
+            file_name: full path name to the uploaded file. */
+       void (*upload)(struct mg_connection *, const char *file_name);
+#endif
+
+       /* Called when civetweb is about to send HTTP error to the client.
+          Implementing this callback allows to create custom error pages.
+          Parameters:
+            status: HTTP error status code.
+          Return value:
+            1: run civetweb error handler.
+            0: callback already handled the error. */
+       int (*http_error)(struct mg_connection *, int status);
+
+       /* Called after civetweb context has been created, before requests
+          are processed.
+          Parameters:
+            ctx: context handle */
+       void (*init_context)(const struct mg_context *ctx);
+
+       /* Called when a new worker thread is initialized.
+          Parameters:
+            ctx: context handle
+            thread_type:
+              0 indicates the master thread
+              1 indicates a worker thread handling client connections
+              2 indicates an internal helper thread (timer thread)
+              */
+       void (*init_thread)(const struct mg_context *ctx, int thread_type);
+
+       /* Called when civetweb context is deleted.
+          Parameters:
+            ctx: context handle */
+       void (*exit_context)(const struct mg_context *ctx);
+};
+
+
+/* Start web server.
+
+   Parameters:
+     callbacks: mg_callbacks structure with user-defined callbacks.
+     options: NULL terminated list of option_name, option_value pairs that
+              specify Civetweb configuration parameters.
+
+   Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
+      processing is required for these, signal handlers must be set up
+      after calling mg_start().
+
+
+   Example:
+     const char *options[] = {
+       "document_root", "/var/www",
+       "listening_ports", "80,443s",
+       NULL
+     };
+     struct mg_context *ctx = mg_start(&my_func, NULL, options);
+
+   Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
+   for the list of valid option and their possible values.
+
+   Return:
+     web server context, or NULL on error. */
+CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
+                                         void *user_data,
+                                         const char **configuration_options);
+
+
+/* Stop the web server.
+
+   Must be called last, when an application wants to stop the web server and
+   release all associated resources. This function blocks until all Civetweb
+   threads are stopped. Context pointer becomes invalid. */
+CIVETWEB_API void mg_stop(struct mg_context *);
+
+
+/* mg_request_handler
+
+   Called when a new request comes in.  This callback is URI based
+   and configured with mg_set_request_handler().
+
+   Parameters:
+      conn: current connection information.
+      cbdata: the callback data configured with mg_set_request_handler().
+   Returns:
+      0: the handler could not handle the request, so fall through.
+      1 - 999: the handler processed the request. The return code is
+               stored as a HTTP status code for the access log. */
+typedef int (*mg_request_handler)(struct mg_connection *conn, void *cbdata);
+
+
+/* mg_set_request_handler
+
+   Sets or removes a URI mapping for a request handler.
+   This function uses mg_lock_context internally.
+
+   URI's are ordered and prefixed URI's are supported. For example,
+   consider two URIs: /a/b and /a
+           /a   matches /a
+           /a/b matches /a/b
+           /a/c matches /a
+
+   Parameters:
+      ctx: server context
+      uri: the URI (exact or pattern) for the handler
+      handler: the callback handler to use when the URI is requested.
+               If NULL, an already registered handler for this URI will be
+   removed.
+               The URI used to remove a handler must match exactly the one used
+   to
+               register it (not only a pattern match).
+      cbdata: the callback data to give to the handler when it is called. */
+CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx,
+                                         const char *uri,
+                                         mg_request_handler handler,
+                                         void *cbdata);
+
+
+/* Callback types for websocket handlers in C/C++.
+
+   mg_websocket_connect_handler
+       Is called when the client intends to establish a websocket connection,
+       before websocket handshake.
+       Return value:
+         0: civetweb proceeds with websocket handshake.
+         1: connection is closed immediately.
+
+   mg_websocket_ready_handler
+       Is called when websocket handshake is successfully completed, and
+       connection is ready for data exchange.
+
+   mg_websocket_data_handler
+       Is called when a data frame has been received from the client.
+       Parameters:
+         bits: first byte of the websocket frame, see websocket RFC at
+               http://tools.ietf.org/html/rfc6455, section 5.2
+         data, data_len: payload, with mask (if any) already applied.
+       Return value:
+         1: keep this websocket connection open.
+         0: close this websocket connection.
+
+   mg_connection_close_handler
+       Is called, when the connection is closed.*/
+typedef int (*mg_websocket_connect_handler)(const struct mg_connection *,
+                                            void *);
+typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
+typedef int (*mg_websocket_data_handler)(struct mg_connection *,
+                                         int,
+                                         char *,
+                                         size_t,
+                                         void *);
+typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
+                                           void *);
+
+
+/* mg_set_websocket_handler
+
+   Set or remove handler functions for websocket connections.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void
+mg_set_websocket_handler(struct mg_context *ctx,
+                         const char *uri,
+                         mg_websocket_connect_handler connect_handler,
+                         mg_websocket_ready_handler ready_handler,
+                         mg_websocket_data_handler data_handler,
+                         mg_websocket_close_handler close_handler,
+                         void *cbdata);
+
+
+/* mg_authorization_handler
+
+   Some description here
+
+   Parameters:
+      conn: current connection information.
+      cbdata: the callback data configured with mg_set_request_handler().
+   Returns:
+      0: access denied
+      1: access granted
+ */
+typedef int (*mg_authorization_handler)(struct mg_connection *conn,
+                                        void *cbdata);
+
+
+/* mg_set_auth_handler
+
+   Sets or removes a URI mapping for an authorization handler.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
+                                      const char *uri,
+                                      mg_authorization_handler handler,
+                                      void *cbdata);
+
+
+/* Get the value of particular configuration parameter.
+   The value returned is read-only. Civetweb does not allow changing
+   configuration at run time.
+   If given parameter name is not valid, NULL is returned. For valid
+   names, return value is guaranteed to be non-NULL. If parameter is not
+   set, zero-length string is returned. */
+CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx,
+                                       const char *name);
+
+
+/* Get context from connection. */
+CIVETWEB_API struct mg_context *
+mg_get_context(const struct mg_connection *conn);
+
+
+/* Get user data passed to mg_start from context. */
+CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
+
+
+/* Set user data for the current connection. */
+CIVETWEB_API void mg_set_user_connection_data(struct mg_connection *conn,
+                                              void *data);
+
+
+/* Get user data set for the current connection. */
+CIVETWEB_API void *
+mg_get_user_connection_data(const struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* Return array of strings that represent valid configuration options.
+   For each option, option name and default value is returned, i.e. the
+   number of entries in the array equals to number_of_options x 2.
+   Array is NULL terminated. */
+/* Deprecated: Use mg_get_valid_options instead. */
+CIVETWEB_API const char **mg_get_valid_option_names(void);
+#endif
+
+
+struct mg_option {
+       const char *name;
+       int type;
+       const char *default_value;
+};
+
+
+enum {
+       CONFIG_TYPE_UNKNOWN = 0x0,
+       CONFIG_TYPE_NUMBER = 0x1,
+       CONFIG_TYPE_STRING = 0x2,
+       CONFIG_TYPE_FILE = 0x3,
+       CONFIG_TYPE_DIRECTORY = 0x4,
+       CONFIG_TYPE_BOOLEAN = 0x5,
+       CONFIG_TYPE_EXT_PATTERN = 0x6
+};
+
+
+/* Return array of struct mg_option, representing all valid configuration
+   options of civetweb.c.
+   The array is terminated by a NULL name option. */
+CIVETWEB_API const struct mg_option *mg_get_valid_options(void);
+
+
+struct mg_server_ports {
+       int protocol;    /* 1 = IPv4, 2 = IPv6, 3 = both */
+       int port;        /* port number */
+       int is_ssl;      /* https port: 0 = no, 1 = yes */
+       int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
+       int _reserved1;
+       int _reserved2;
+       int _reserved3;
+       int _reserved4;
+};
+
+
+/* Get the list of ports that civetweb is listening on.
+   The parameter size is the size of the ports array in elements.
+   The caller is responsibility to allocate the required memory.
+   This function returns the number of struct mg_server_ports elements
+   filled in, or <0 in case of an error. */
+CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx,
+                                     int size,
+                                     struct mg_server_ports *ports);
+
+
+/* Deprecated: Use mg_get_server_ports instead. */
+CIVETWEB_API size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl);
+
+
+/* Add, edit or delete the entry in the passwords file.
+
+   This function allows an application to manipulate .htpasswd files on the
+   fly by adding, deleting and changing user records. This is one of the
+   several ways of implementing authentication on the server side. For another,
+   cookie-based way please refer to the examples/chat in the source tree.
+
+   If password is not NULL, entry is added (or modified if already exists).
+   If password is NULL, entry is deleted.
+
+   Return:
+     1 on success, 0 on error. */
+CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
+                                          const char *domain,
+                                          const char *user,
+                                          const char *password);
+
+
+/* Return information associated with the request. */
+CIVETWEB_API const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *);
+
+
+/* Send data to the client.
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len);
+
+
+/* Send data to a websocket client wrapped in a websocket frame.  Uses
+   mg_lock_connection to ensure that the transmission is not interrupted,
+   i.e., when the application is proactively communicating and responding to
+   a request simultaneously.
+
+   Send data to a websocket client wrapped in a websocket frame.
+   This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+CIVETWEB_API int mg_websocket_write(struct mg_connection *conn,
+                                    int opcode,
+                                    const char *data,
+                                    size_t data_len);
+
+
+/* Send data to a websocket server wrapped in a masked websocket frame.  Uses
+   mg_lock_connection to ensure that the transmission is not interrupted,
+   i.e., when the application is proactively communicating and responding to
+   a request simultaneously.
+
+   Send data to a websocket server wrapped in a masked websocket frame.
+   This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn,
+                                           int opcode,
+                                           const char *data,
+                                           size_t data_len);
+
+
+/* Blocks until unique access is obtained to this connection. Intended for use
+   with websockets only.
+   Invoke this before mg_write or mg_printf when communicating with a
+   websocket if your code has server-initiated communication as well as
+   communication in direct response to a message. */
+CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
+CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+#define mg_lock mg_lock_connection
+#define mg_unlock mg_unlock_connection
+#endif
+
+
+/* Lock server context.  This lock may be used to protect resources
+   that are shared between different connection/worker threads. */
+CIVETWEB_API void mg_lock_context(struct mg_context *ctx);
+CIVETWEB_API void mg_unlock_context(struct mg_context *ctx);
+
+
+/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
+enum {
+       WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+       WEBSOCKET_OPCODE_TEXT = 0x1,
+       WEBSOCKET_OPCODE_BINARY = 0x2,
+       WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+       WEBSOCKET_OPCODE_PING = 0x9,
+       WEBSOCKET_OPCODE_PONG = 0xa
+};
+
+
+/* Macros for enabling compiler-specific checks for printf-like arguments. */
+#undef PRINTF_FORMAT_STRING
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <sal.h>
+#if defined(_MSC_VER) && _MSC_VER > 1400
+#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
+#else
+#define PRINTF_FORMAT_STRING(s) __format_string s
+#endif
+#else
+#define PRINTF_FORMAT_STRING(s) s
+#endif
+
+#ifdef __GNUC__
+#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
+#else
+#define PRINTF_ARGS(x, y)
+#endif
+
+
+/* Send data to the client using printf() semantics.
+   Works exactly like mg_write(), but allows to do message formatting. */
+CIVETWEB_API int mg_printf(struct mg_connection *,
+                           PRINTF_FORMAT_STRING(const char *fmt),
+                           ...) PRINTF_ARGS(2, 3);
+
+
+/* Send contents of the entire file together with HTTP headers. */
+CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
+
+/* Send contents of the entire file together with HTTP headers.
+   Parameters:
+     conn: Current connection information.
+     path: Full path to the file to send.
+     mime_type: Content-Type for file.  NULL will cause the type to be
+                looked up by the file extension.
+*/
+CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn,
+                                    const char *path,
+                                    const char *mime_type);
+
+/* Store body data into a file. */
+CIVETWEB_API long long mg_store_body(struct mg_connection *conn,
+                                     const char *path);
+/* Read entire request body and stor it in a file "path".
+   Return:
+     < 0   Error
+     >= 0  Number of bytes stored in file "path".
+*/
+
+
+/* Read data from the remote end, return number of bytes read.
+   Return:
+     0     connection has been closed by peer. No more data could be read.
+     < 0   read error. No more data could be read from the connection.
+     > 0   number of bytes read into the buffer. */
+CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
+
+
+/* Get the value of particular HTTP header.
+
+   This is a helper function. It traverses request_info->http_headers array,
+   and if the header is present in the array, returns its value. If it is
+   not present, NULL is returned. */
+CIVETWEB_API const char *mg_get_header(const struct mg_connection *,
+                                       const char *name);
+
+
+/* Get a value of particular form variable.
+
+   Parameters:
+     data: pointer to form-uri-encoded buffer. This could be either POST data,
+           or request_info.query_string.
+     data_len: length of the encoded data.
+     var_name: variable name to decode from the buffer
+     dst: destination buffer for the decoded variable
+     dst_len: length of the destination buffer
+
+   Return:
+     On success, length of the decoded variable.
+     On error:
+        -1 (variable not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            decoded variable).
+
+   Destination buffer is guaranteed to be '\0' - terminated if it is not
+   NULL or zero length. */
+CIVETWEB_API int mg_get_var(const char *data,
+                            size_t data_len,
+                            const char *var_name,
+                            char *dst,
+                            size_t dst_len);
+
+
+/* Get a value of particular form variable.
+
+   Parameters:
+     data: pointer to form-uri-encoded buffer. This could be either POST data,
+           or request_info.query_string.
+     data_len: length of the encoded data.
+     var_name: variable name to decode from the buffer
+     dst: destination buffer for the decoded variable
+     dst_len: length of the destination buffer
+     occurrence: which occurrence of the variable, 0 is the first, 1 the
+                 second...
+                this makes it possible to parse a query like
+                b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1
+
+   Return:
+     On success, length of the decoded variable.
+     On error:
+        -1 (variable not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            decoded variable).
+
+   Destination buffer is guaranteed to be '\0' - terminated if it is not
+   NULL or zero length. */
+CIVETWEB_API int mg_get_var2(const char *data,
+                             size_t data_len,
+                             const char *var_name,
+                             char *dst,
+                             size_t dst_len,
+                             size_t occurrence);
+
+
+/* Fetch value of certain cookie variable into the destination buffer.
+
+   Destination buffer is guaranteed to be '\0' - terminated. In case of
+   failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
+   parameter. This function returns only first occurrence.
+
+   Return:
+     On success, value length.
+     On error:
+        -1 (either "Cookie:" header is not present at all or the requested
+            parameter is not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            value). */
+CIVETWEB_API int mg_get_cookie(const char *cookie,
+                               const char *var_name,
+                               char *buf,
+                               size_t buf_len);
+
+
+/* Download data from the remote web server.
+     host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
+     port: port number, e.g. 80.
+     use_ssl: wether to use SSL connection.
+     error_buffer, error_buffer_size: error message placeholder.
+     request_fmt,...: HTTP request.
+   Return:
+     On success, valid pointer to the new connection, suitable for mg_read().
+     On error, NULL. error_buffer contains error message.
+   Example:
+     char ebuf[100];
+     struct mg_connection *conn;
+     conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
+                        "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+ */
+CIVETWEB_API struct mg_connection *
+mg_download(const char *host,
+            int port,
+            int use_ssl,
+            char *error_buffer,
+            size_t error_buffer_size,
+            PRINTF_FORMAT_STRING(const char *request_fmt),
+            ...) PRINTF_ARGS(6, 7);
+
+
+/* Close the connection opened by mg_download(). */
+CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE)
+/* File upload functionality. Each uploaded file gets saved into a temporary
+   file and MG_UPLOAD event is sent.
+   Return number of uploaded files.
+   Deprecated: Use mg_handle_form_request instead. */
+CIVETWEB_API int mg_upload(struct mg_connection *conn,
+                           const char *destination_dir);
+#endif
+
+
+/* This structure contains callback functions for handling form fields.
+   It is used as an argument to mg_handle_form_request. */
+struct mg_form_data_handler {
+       /* This callback function is called, if a new field has been found.
+        * The return value of this callback is used to define how the field
+        * should be processed.
+        *
+        * Parameters:
+        *   key: Name of the field ("name" property of the HTML input field).
+        *   filename: Name of a file to upload, at the client computer.
+        *             Only set for input fields of type "file", otherwise NULL.
+        *   path: Output parameter: File name (incl. path) to store the file
+        *         at the server computer. Only used if FORM_FIELD_STORAGE_STORE
+        *         is returned by this callback. Existing files will be
+        *         overwritten.
+        *   pathlen: Length of the buffer for path.
+        *   user_data: Value of the member user_data of mg_form_data_handler
+        *
+        * Return value:
+        *   The callback must return the intended storage for this field
+        *   (See FORM_FIELD_STORAGE_*).
+        */
+       int (*field_found)(const char *key,
+                          const char *filename,
+                          char *path,
+                          size_t pathlen,
+                          void *user_data);
+
+       /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
+        * this callback will receive the field data.
+        *
+        * Parameters:
+        *   key: Name of the field ("name" property of the HTML input field).
+        *   value: Value of the input field.
+        *   user_data: Value of the member user_data of mg_form_data_handler
+        *
+        * Return value:
+        *   TODO: Needs to be defined.
+        */
+       int (*field_get)(const char *key,
+                        const char *value,
+                        size_t valuelen,
+                        void *user_data);
+
+       /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
+        * the data will be stored into a file. If the file has been written
+        * successfully, this callback will be called. This callback will
+        * not be called for only partially uploaded files. The
+        * mg_handle_form_request function will either store the file completely
+        * and call this callback, or it will remove any partial content and
+        * not call this callback function.
+        *
+        * Parameters:
+        *   path: Path of the file stored at the server.
+        *   file_size: Size of the stored file in bytes.
+        *   user_data: Value of the member user_data of mg_form_data_handler
+        *
+        * Return value:
+        *   TODO: Needs to be defined.
+        */
+       int (*field_store)(const char *path, long long file_size, void *user_data);
+
+       /* User supplied argument, passed to all callback functions. */
+       void *user_data;
+};
+
+
+/* Return values definition for the "field_found" callback in
+ * mg_form_data_handler. */
+enum {
+       /* Skip this field (neither get nor store it). Continue with the
+     * next field. */
+       FORM_FIELD_STORAGE_SKIP = 0x0,
+       /* Get the field value. */
+       FORM_FIELD_STORAGE_GET = 0x1,
+       /* Store the field value into a file. */
+       FORM_FIELD_STORAGE_STORE = 0x2,
+       /* Stop parsing this request. Skip the remaining fields. */
+       FORM_FIELD_STORAGE_ABORT = 0x10
+};
+
+
+/* Process form data.
+ * Returns the number of fields handled, or < 0 in case of an error.
+ * Note: It is possible that several fields are already handled successfully
+ * (e.g., stored into files), before the request handling is stopped with an
+ * error. In this case a number < 0 is returned as well.
+ * In any case, it is the duty of the caller to remove files once they are
+ * no longer required. */
+CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn,
+                                        struct mg_form_data_handler *fdh);
+
+
+/* Convenience function -- create detached thread.
+   Return: 0 on success, non-0 on error. */
+typedef void *(*mg_thread_func_t)(void *);
+CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p);
+
+
+/* Return builtin mime type for the given file name.
+   For unrecognized extensions, "text/plain" is returned. */
+CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
+
+
+/* Get text representation of HTTP status code. */
+CIVETWEB_API const char *mg_get_response_code_text(struct mg_connection *conn,
+                                                   int response_code);
+
+
+/* Return CivetWeb version. */
+CIVETWEB_API const char *mg_version(void);
+
+
+/* URL-decode input buffer into destination buffer.
+   0-terminate the destination buffer.
+   form-url-encoded data differs from URI encoding in a way that it
+   uses '+' as character for space, see RFC 1866 section 8.2.1
+   http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
+   Return: length of the decoded data, or -1 if dst buffer is too small. */
+CIVETWEB_API int mg_url_decode(const char *src,
+                               int src_len,
+                               char *dst,
+                               int dst_len,
+                               int is_form_url_encoded);
+
+
+/* URL-encode input buffer into destination buffer.
+   returns the length of the resulting buffer or -1
+   is the buffer is too small. */
+CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
+
+
+/* MD5 hash given strings.
+   Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
+   ASCIIz strings. When function returns, buf will contain human-readable
+   MD5 hash. Example:
+     char buf[33];
+     mg_md5(buf, "aa", "bb", NULL); */
+CIVETWEB_API char *mg_md5(char buf[33], ...);
+
+
+/* Print error message to the opened error log stream.
+   This utilizes the provided logging configuration.
+     conn: connection
+     fmt: format string without the line return
+     ...: variable argument list
+   Example:
+     mg_cry(conn,"i like %s", "logging"); */
+CIVETWEB_API void mg_cry(const struct mg_connection *conn,
+                         PRINTF_FORMAT_STRING(const char *fmt),
+                         ...) PRINTF_ARGS(2, 3);
+
+
+/* utility methods to compare two buffers, case incensitive. */
+CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2);
+CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
+
+
+/* Connect to a websocket as a client
+   Parameters:
+     host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or
+   "localhost"
+     port: server port
+     use_ssl: make a secure connection to server
+     error_buffer, error_buffer_size: buffer for an error message
+     path: server path you are trying to connect to, i.e. if connection to
+   localhost/app, path should be "/app"
+     origin: value of the Origin HTTP header
+     data_func: callback that should be used when data is received from the
+   server
+     user_data: user supplied argument
+
+   Return:
+     On success, valid mg_connection object.
+     On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *
+mg_connect_websocket_client(const char *host,
+                            int port,
+                            int use_ssl,
+                            char *error_buffer,
+                            size_t error_buffer_size,
+                            const char *path,
+                            const char *origin,
+                            mg_websocket_data_handler data_func,
+                            mg_websocket_close_handler close_func,
+                            void *user_data);
+
+
+/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
+   Parameters:
+     host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or
+   "localhost"
+     port: server port
+     use_ssl: make a secure connection to server
+     error_buffer, error_buffer_size: buffer for an error message
+
+   Return:
+     On success, valid mg_connection object.
+     On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
+                                                     int port,
+                                                     int use_ssl,
+                                                     char *error_buffer,
+                                                     size_t error_buffer_size);
+
+
+struct mg_client_options {
+       const char *host;
+       int port;
+       const char *client_cert;
+       const char *server_cert;
+       /* TODO: add more data */
+};
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+                         char *error_buffer,
+                         size_t error_buffer_size);
+
+
+enum { TIMEOUT_INFINITE = -1 };
+
+
+/* Wait for a response from the server
+   Parameters:
+     conn: connection
+     ebuf, ebuf_len: error message placeholder.
+     timeout: time to wait for a response in milliseconds (if < 0 then wait
+   forever)
+
+   Return:
+     On success, >= 0
+     On error/timeout, < 0
+*/
+CIVETWEB_API int mg_get_response(struct mg_connection *conn,
+                                 char *ebuf,
+                                 size_t ebuf_len,
+                                 int timeout);
+
+
+/* Check which features where set when civetweb has been compiled.
+   Parameters:
+     feature: specifies which feature should be checked
+         1  serve files (NO_FILES not set)
+         2  support HTTPS (NO_SSL not set)
+         4  support CGI (NO_CGI not set)
+         8  support IPv6 (USE_IPV6 set)
+        16  support WebSocket (USE_WEBSOCKET set)
+        32  support Lua scripts and Lua server pages (USE_LUA is set)
+        64  support server side JavaScript (USE_DUKTAPE is set)
+       128  support caching (NO_CACHING not set)
+       The result is undefined for all other feature values.
+
+   Return:
+     If feature is available > 0
+     If feature is not available = 0
+*/
+CIVETWEB_API unsigned mg_check_feature(unsigned feature);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CIVETWEB_HEADER_INCLUDED */
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/handle_form.inl b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/handle_form.inl
new file mode 100644 (file)
index 0000000..70720e2
--- /dev/null
@@ -0,0 +1,793 @@
+/* Copyright (c) 2016 the Civetweb developers
+ *
+ * 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.
+ */
+
+
+static int
+url_encoded_field_found(const struct mg_connection *conn,
+                        const char *key,
+                        size_t key_len,
+                        const char *filename,
+                        size_t filename_len,
+                        char *path,
+                        size_t path_len,
+                        struct mg_form_data_handler *fdh)
+{
+       char key_dec[1024];
+       char filename_dec[1024];
+       int key_dec_len;
+       int filename_dec_len;
+       int ret;
+
+       key_dec_len =
+           mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+       if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
+               return FORM_FIELD_STORAGE_SKIP;
+       }
+
+       if (filename) {
+               filename_dec_len = mg_url_decode(filename,
+                                                (int)filename_len,
+                                                filename_dec,
+                                                (int)sizeof(filename_dec),
+                                                1);
+
+               if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
+                   || (filename_dec_len < 0)) {
+                       /* Log error message and skip this field. */
+                       mg_cry(conn, "%s: Cannot decode filename", __func__);
+                       return FORM_FIELD_STORAGE_SKIP;
+               }
+       } else {
+               filename_dec[0] = 0;
+       }
+
+       ret =
+           fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
+
+       if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
+               if (fdh->field_get == NULL) {
+                       mg_cry(conn, "%s: Function \"Get\" not available", __func__);
+                       return FORM_FIELD_STORAGE_SKIP;
+               }
+       }
+       if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
+               if (fdh->field_store == NULL) {
+                       mg_cry(conn, "%s: Function \"Store\" not available", __func__);
+                       return FORM_FIELD_STORAGE_SKIP;
+               }
+       }
+
+       return ret;
+}
+
+
+static int
+url_encoded_field_get(const struct mg_connection *conn,
+                      const char *key,
+                      size_t key_len,
+                      const char *value,
+                      size_t value_len,
+                      struct mg_form_data_handler *fdh)
+{
+       char key_dec[1024];
+
+       char *value_dec = mg_malloc(value_len + 1);
+       int value_dec_len;
+
+       if (!value_dec) {
+               /* Log error message and stop parsing the form data. */
+               mg_cry(conn,
+                      "%s: Not enough memory (required: %lu)",
+                      __func__,
+                      (unsigned long)(value_len + 1));
+               return FORM_FIELD_STORAGE_ABORT;
+       }
+
+       mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+       value_dec_len =
+           mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
+
+       return fdh->field_get(key_dec,
+                             value_dec,
+                             (size_t)value_dec_len,
+                             fdh->user_data);
+}
+
+
+static int
+field_stored(const struct mg_connection *conn,
+             const char *path,
+             long long file_size,
+             struct mg_form_data_handler *fdh)
+{
+       /* Equivalent to "upload" callback of "mg_upload". */
+
+       (void)conn; /* we do not need mg_cry here, so conn is currently unused */
+
+       return fdh->field_store(path, file_size, fdh->user_data);
+}
+
+
+static const char *
+search_boundary(const char *buf,
+                size_t buf_len,
+                const char *boundary,
+                size_t boundary_len)
+{
+       /* We must do a binary search here, not a string search, since the buffer
+        * may contain '\x00' bytes, if binary data is transferred. */
+       int clen = (int)buf_len - (int)boundary_len - 4;
+       int i;
+
+       for (i = 0; i <= clen; i++) {
+               if (!memcmp(buf + i, "\r\n--", 4)) {
+                       if (!memcmp(buf + i + 4, boundary, boundary_len)) {
+                               return buf + i;
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+int
+mg_handle_form_request(struct mg_connection *conn,
+                       struct mg_form_data_handler *fdh)
+{
+       const char *content_type;
+       char path[512];
+       char buf[1024];
+       int field_storage;
+       int buf_fill = 0;
+       int r;
+       int field_count = 0;
+       struct file fstore = STRUCT_FILE_INITIALIZER;
+       int64_t file_size = 0; /* init here, to a avoid a false positive
+                                "uninitialized variable used" warning */
+
+       int has_body_data =
+           (conn->request_info.content_length > 0) || (conn->is_chunked);
+
+       /* There are three ways to encode data from a HTML form:
+        * 1) method: GET (default)
+        *    The form data is in the HTTP query string.
+        * 2) method: POST, enctype: "application/x-www-form-urlencoded"
+        *    The form data is in the request body.
+        *    The body is url encoded (the default encoding for POST).
+        * 3) method: POST, enctype: "multipart/form-data".
+        *    The form data is in the request body of a multipart message.
+        *    This is the typical way to handle file upload from a form.
+        */
+
+       if (!has_body_data) {
+               const char *data;
+
+               if (strcmp(conn->request_info.request_method, "GET")) {
+                       /* No body data, but not a GET request.
+                        * This is not a valid form request. */
+                       return -1;
+               }
+
+               /* GET request: form data is in the query string. */
+               /* The entire data has already been loaded, so there is no nead to
+                * call mg_read. We just need to split the query string into key-value
+                * pairs. */
+               data = conn->request_info.query_string;
+               if (!data) {
+                       /* No query string. */
+                       return -1;
+               }
+
+               /* Split data in a=1&b=xy&c=3&c=4 ... */
+               while (*data) {
+                       const char *val = strchr(data, '=');
+                       const char *next;
+                       ptrdiff_t keylen, vallen;
+
+                       if (!val) {
+                               break;
+                       }
+                       keylen = val - data;
+
+                       /* In every "field_found" callback we ask what to do with the
+                        * data ("field_storage"). This could be:
+                        * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
+                        * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
+                        *                              callback function
+                        * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
+                        * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
+                        *                               (for parsing long data on the fly)
+                        *                               (currently not implemented)
+                        * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
+                        */
+                       memset(path, 0, sizeof(path));
+                       field_count++;
+                       field_storage = url_encoded_field_found(conn,
+                                                               data,
+                                                               (size_t)keylen,
+                                                               NULL,
+                                                               0,
+                                                               path,
+                                                               sizeof(path) - 1,
+                                                               fdh);
+
+                       val++;
+                       next = strchr(val, '&');
+                       if (next) {
+                               vallen = next - val;
+                               next++;
+                       } else {
+                               vallen = (ptrdiff_t)strlen(val);
+                               next = val + vallen;
+                       }
+
+                       if (field_storage == FORM_FIELD_STORAGE_GET) {
+                               /* Call callback */
+                               url_encoded_field_get(
+                                   conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
+                       }
+                       if (field_storage == FORM_FIELD_STORAGE_STORE) {
+                               /* Store the content to a file */
+                               if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+                                       fstore.fp = NULL;
+                               }
+                               file_size = 0;
+                               if (fstore.fp != NULL) {
+                                       size_t n =
+                                           (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
+                                       if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
+                                               mg_cry(conn,
+                                                      "%s: Cannot write file %s",
+                                                      __func__,
+                                                      path);
+                                               fclose(fstore.fp);
+                                               fstore.fp = NULL;
+                                               remove_bad_file(conn, path);
+                                       }
+                                       file_size += (int64_t)n;
+
+                                       if (fstore.fp) {
+                                               r = fclose(fstore.fp);
+                                               if (r == 0) {
+                                                       /* stored successfully */
+                                                       field_stored(conn, path, file_size, fdh);
+                                               } else {
+                                                       mg_cry(conn,
+                                                              "%s: Error saving file %s",
+                                                              __func__,
+                                                              path);
+                                                       remove_bad_file(conn, path);
+                                               }
+                                               fstore.fp = NULL;
+                                       }
+
+                               } else {
+                                       mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+                               }
+                       }
+
+                       /* if (field_storage == FORM_FIELD_STORAGE_READ) { */
+                       /* The idea of "field_storage=read" is to let the API user read
+                        * data chunk by chunk and to some data processing on the fly.
+                        * This should avoid the need to store data in the server:
+                        * It should neither be stored in memory, like
+                        * "field_storage=get" does, nor in a file like
+                        * "field_storage=store".
+                        * However, for a "GET" request this does not make any much
+                        * sense, since the data is already stored in memory, as it is
+                        * part of the query string.
+                        */
+                       /* } */
+
+                       if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+                           == FORM_FIELD_STORAGE_ABORT) {
+                               /* Stop parsing the request */
+                               break;
+                       }
+
+                       /* Proceed to next entry */
+                       data = next;
+               }
+
+               return field_count;
+       }
+
+       content_type = mg_get_header(conn, "Content-Type");
+
+       if (!content_type
+           || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
+           || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
+               /* The form data is in the request body data, encoded in key/value
+                * pairs. */
+               int all_data_read = 0;
+
+               /* Read body data and split it in keys and values.
+                * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
+                * Here we use "POST", and read the data from the request body.
+                * The data read on the fly, so it is not required to buffer the
+                * entire request in memory before processing it. */
+               for (;;) {
+                       const char *val;
+                       const char *next;
+                       ptrdiff_t keylen, vallen;
+                       ptrdiff_t used;
+                       int end_of_key_value_pair_found = 0;
+                       int get_block;
+
+                       if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+                               size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+                               r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+                               if (r < 0) {
+                                       /* read error */
+                                       return -1;
+                               }
+                               if (r != (int)to_read) {
+                                       /* TODO: Create a function to get "all_data_read" from
+                                        * the conn object. All data is read if the Content-Length
+                                        * has been reached, or if chunked encoding is used and
+                                        * the end marker has been read, or if the connection has
+                                        * been closed. */
+                                       all_data_read = 1;
+                               }
+                               buf_fill += r;
+                               buf[buf_fill] = 0;
+                               if (buf_fill < 1) {
+                                       break;
+                               }
+                       }
+
+                       val = strchr(buf, '=');
+
+                       if (!val) {
+                               break;
+                       }
+                       keylen = val - buf;
+                       val++;
+
+                       /* Call callback */
+                       memset(path, 0, sizeof(path));
+                       field_count++;
+                       field_storage = url_encoded_field_found(conn,
+                                                               buf,
+                                                               (size_t)keylen,
+                                                               NULL,
+                                                               0,
+                                                               path,
+                                                               sizeof(path) - 1,
+                                                               fdh);
+
+                       if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+                           == FORM_FIELD_STORAGE_ABORT) {
+                               /* Stop parsing the request */
+                               break;
+                       }
+
+                       if (field_storage == FORM_FIELD_STORAGE_STORE) {
+                               if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+                                       fstore.fp = NULL;
+                               }
+                               file_size = 0;
+                               if (!fstore.fp) {
+                                       mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+                               }
+                       }
+
+                       get_block = 0;
+                       /* Loop to read values larger than sizeof(buf)-keylen-2 */
+                       do {
+                               next = strchr(val, '&');
+                               if (next) {
+                                       vallen = next - val;
+                                       next++;
+                                       end_of_key_value_pair_found = 1;
+                               } else {
+                                       vallen = (ptrdiff_t)strlen(val);
+                                       next = val + vallen;
+                               }
+
+                               if (field_storage == FORM_FIELD_STORAGE_GET) {
+#if 0
+                                       if (!end_of_key_value_pair_found && !all_data_read) {
+                                               /* This callback will deliver partial contents */
+                                       }
+#else
+                                       (void)all_data_read; /* avoid warning */
+#endif
+
+                                       /* Call callback */
+                                       url_encoded_field_get(conn,
+                                                             ((get_block > 0) ? NULL : buf),
+                                                             ((get_block > 0) ? 0
+                                                                              : (size_t)keylen),
+                                                             val,
+                                                             (size_t)vallen,
+                                                             fdh);
+                                       get_block++;
+                               }
+                               if (fstore.fp) {
+                                       size_t n =
+                                           (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
+                                       if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
+                                               mg_cry(conn,
+                                                      "%s: Cannot write file %s",
+                                                      __func__,
+                                                      path);
+                                               fclose(fstore.fp);
+                                               fstore.fp = NULL;
+                                               remove_bad_file(conn, path);
+                                       }
+                                       file_size += (int64_t)n;
+                               }
+
+                               if (!end_of_key_value_pair_found) {
+                                       used = next - buf;
+                                       memmove(buf,
+                                               buf + (size_t)used,
+                                               sizeof(buf) - (size_t)used);
+                                       buf_fill -= (int)used;
+                                       if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+                                               size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+                                               r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+                                               if (r < 0) {
+                                                       /* read error */
+                                                       return -1;
+                                               }
+                                               if (r != (int)to_read) {
+                                                       /* TODO: Create a function to get "all_data_read"
+                                                        * from the conn object. All data is read if the
+                                                        * Content-Length has been reached, or if chunked
+                                                        * encoding is used and the end marker has been
+                                                        * read, or if the connection has been closed. */
+                                                       all_data_read = 1;
+                                               }
+                                               buf_fill += r;
+                                               buf[buf_fill] = 0;
+                                               if (buf_fill < 1) {
+                                                       break;
+                                               }
+                                               val = buf;
+                                       }
+                               }
+
+                       } while (!end_of_key_value_pair_found);
+
+                       if (fstore.fp) {
+                               r = fclose(fstore.fp);
+                               if (r == 0) {
+                                       /* stored successfully */
+                                       field_stored(conn, path, file_size, fdh);
+                               } else {
+                                       mg_cry(conn, "%s: Error saving file %s", __func__, path);
+                                       remove_bad_file(conn, path);
+                               }
+                               fstore.fp = NULL;
+                       }
+
+                       /* Proceed to next entry */
+                       used = next - buf;
+                       memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+                       buf_fill -= (int)used;
+               }
+
+               return field_count;
+       }
+
+       if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
+               /* The form data is in the request body data, encoded as multipart
+                * content (see https://www.ietf.org/rfc/rfc1867.txt,
+                * https://www.ietf.org/rfc/rfc2388.txt). */
+               const char *boundary;
+               size_t bl;
+               ptrdiff_t used;
+               struct mg_request_info part_header;
+               char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
+               const char *content_disp;
+               const char *next;
+
+               memset(&part_header, 0, sizeof(part_header));
+
+               /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
+               bl = 20;
+               while (content_type[bl] == ' ') {
+                       bl++;
+               }
+
+               /* There has to be a BOUNDARY definition in the Content-Type header */
+               if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
+                       /* Malformed request */
+                       return -1;
+               }
+
+               boundary = content_type + bl + 9;
+               bl = strlen(boundary);
+
+               if (bl + 800 > sizeof(buf)) {
+                       /* Sanity check:  The algorithm can not work if bl >= sizeof(buf),
+                        * and it will not work effectively, if the buf is only a few byte
+                        * larger than bl, or it buf can not hold the multipart header
+                        * plus the boundary.
+                        * Check some reasonable number here, that should be fulfilled by
+                        * any reasonable request from every browser. If it is not
+                        * fulfilled, it might be a hand-made request, intended to
+                        * interfere with the algorithm. */
+                       return -1;
+               }
+
+               for (;;) {
+                       size_t towrite, n;
+                       int get_block;
+
+                       r = mg_read(conn,
+                                   buf + (size_t)buf_fill,
+                                   sizeof(buf) - 1 - (size_t)buf_fill);
+                       if (r < 0) {
+                               /* read error */
+                               return -1;
+                       }
+                       buf_fill += r;
+                       buf[buf_fill] = 0;
+                       if (buf_fill < 1) {
+                               /* No data */
+                               return -1;
+                       }
+
+                       if (buf[0] != '-' || buf[1] != '-') {
+                               /* Malformed request */
+                               return -1;
+                       }
+                       if (strncmp(buf + 2, boundary, bl)) {
+                               /* Malformed request */
+                               return -1;
+                       }
+                       if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
+                               /* Every part must end with \r\n, if there is another part.
+                                * The end of the request has an extra -- */
+                               if (((size_t)buf_fill != (size_t)(bl + 6))
+                                   || (strncmp(buf + bl + 2, "--\r\n", 4))) {
+                                       /* Malformed request */
+                                       return -1;
+                               }
+                               /* End of the request */
+                               break;
+                       }
+
+                       /* Next, we need to get the part header: Read until \r\n\r\n */
+                       hbuf = buf + bl + 4;
+                       hend = strstr(hbuf, "\r\n\r\n");
+                       if (!hend) {
+                               /* Malformed request */
+                               return -1;
+                       }
+
+                       parse_http_headers(&hbuf, &part_header);
+                       if ((hend + 2) != hbuf) {
+                               /* Malformed request */
+                               return -1;
+                       }
+
+                       /* Skip \r\n\r\n */
+                       hend += 4;
+
+                       /* According to the RFC, every part has to have a header field like:
+                        * Content-Disposition: form-data; name="..." */
+                       content_disp = get_header(&part_header, "Content-Disposition");
+                       if (!content_disp) {
+                               /* Malformed request */
+                               return -1;
+                       }
+
+                       /* Get the mandatory name="..." part of the Content-Disposition
+                        * header. */
+                       nbeg = strstr(content_disp, "name=\"");
+                       if (!nbeg) {
+                               /* Malformed request */
+                               return -1;
+                       }
+                       nbeg += 6;
+                       nend = strchr(nbeg, '\"');
+                       if (!nend) {
+                               /* Malformed request */
+                               return -1;
+                       }
+
+                       /* Get the optional filename="..." part of the Content-Disposition
+                        * header. */
+                       fbeg = strstr(content_disp, "filename=\"");
+                       if (fbeg) {
+                               fbeg += 10;
+                               fend = strchr(fbeg, '\"');
+                               if (!fend) {
+                                       /* Malformed request (the filename field is optional, but if
+                                        * it exists, it needs to be terminated correctly). */
+                                       return -1;
+                               }
+
+                               /* TODO: check Content-Type */
+                               /* Content-Type: application/octet-stream */
+
+                       } else {
+                               fend = fbeg;
+                       }
+
+                       memset(path, 0, sizeof(path));
+                       field_count++;
+                       field_storage = url_encoded_field_found(conn,
+                                                               nbeg,
+                                                               (size_t)(nend - nbeg),
+                                                               fbeg,
+                                                               (size_t)(fend - fbeg),
+                                                               path,
+                                                               sizeof(path) - 1,
+                                                               fdh);
+
+                       /* If the boundary is already in the buffer, get the address,
+                        * otherwise next will be NULL. */
+                       next = search_boundary(hbuf,
+                                              (size_t)((buf - hbuf) + buf_fill),
+                                              boundary,
+                                              bl);
+
+                       if (field_storage == FORM_FIELD_STORAGE_STORE) {
+                               /* Store the content to a file */
+                               if (mg_fopen(conn, path, "wb", &fstore) == 0) {
+                                       fstore.fp = NULL;
+                               }
+                               file_size = 0;
+
+                               if (!fstore.fp) {
+                                       mg_cry(conn, "%s: Cannot create file %s", __func__, path);
+                               }
+                       }
+
+                       get_block = 0;
+                       while (!next) {
+                               /* Set "towrite" to the number of bytes available
+                                * in the buffer */
+                               towrite = (size_t)(buf - hend + buf_fill);
+                               /* Subtract the boundary length, to deal with
+                                * cases the boundary is only partially stored
+                                * in the buffer. */
+                               towrite -= bl + 4;
+
+                               if (field_storage == FORM_FIELD_STORAGE_GET) {
+                                       url_encoded_field_get(conn,
+                                                             ((get_block > 0) ? NULL : nbeg),
+                                                             ((get_block > 0)
+                                                                  ? 0
+                                                                  : (size_t)(nend - nbeg)),
+                                                             hend,
+                                                             towrite,
+                                                             fdh);
+                                       get_block++;
+                               }
+
+                               if (field_storage == FORM_FIELD_STORAGE_STORE) {
+                                       if (fstore.fp) {
+
+                                               /* Store the content of the buffer. */
+                                               n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
+                                               if ((n != towrite) || (ferror(fstore.fp))) {
+                                                       mg_cry(conn,
+                                                              "%s: Cannot write file %s",
+                                                              __func__,
+                                                              path);
+                                                       fclose(fstore.fp);
+                                                       fstore.fp = NULL;
+                                                       remove_bad_file(conn, path);
+                                               }
+                                               file_size += (int64_t)n;
+                                       }
+                               }
+
+                               memmove(buf, hend + towrite, bl + 4);
+                               buf_fill = (int)(bl + 4);
+                               hend = buf;
+
+                               /* Read new data */
+                               r = mg_read(conn,
+                                           buf + (size_t)buf_fill,
+                                           sizeof(buf) - 1 - (size_t)buf_fill);
+                               if (r < 0) {
+                                       /* read error */
+                                       return -1;
+                               }
+                               buf_fill += r;
+                               buf[buf_fill] = 0;
+                               if (buf_fill < 1) {
+                                       /* No data */
+                                       return -1;
+                               }
+
+                               /* Find boundary */
+                               next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
+                       }
+
+                       towrite = (size_t)(next - hend);
+
+                       if (field_storage == FORM_FIELD_STORAGE_GET) {
+                               /* Call callback */
+                               url_encoded_field_get(conn,
+                                                     ((get_block > 0) ? NULL : nbeg),
+                                                     ((get_block > 0) ? 0
+                                                                      : (size_t)(nend - nbeg)),
+                                                     hend,
+                                                     towrite,
+                                                     fdh);
+                       }
+
+                       if (field_storage == FORM_FIELD_STORAGE_STORE) {
+
+                               if (fstore.fp) {
+                                       n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
+                                       if ((n != towrite) || (ferror(fstore.fp))) {
+                                               mg_cry(conn,
+                                                      "%s: Cannot write file %s",
+                                                      __func__,
+                                                      path);
+                                               fclose(fstore.fp);
+                                               fstore.fp = NULL;
+                                               remove_bad_file(conn, path);
+                                       }
+                                       file_size += (int64_t)n;
+                               }
+                       }
+
+                       if (field_storage == FORM_FIELD_STORAGE_STORE) {
+
+                               if (fstore.fp) {
+                                       r = fclose(fstore.fp);
+                                       if (r == 0) {
+                                               /* stored successfully */
+                                               field_stored(conn, path, file_size, fdh);
+                                       } else {
+                                               mg_cry(conn,
+                                                      "%s: Error saving file %s",
+                                                      __func__,
+                                                      path);
+                                               remove_bad_file(conn, path);
+                                       }
+                                       fstore.fp = NULL;
+                               }
+                       }
+
+                       if ((field_storage & FORM_FIELD_STORAGE_ABORT)
+                           == FORM_FIELD_STORAGE_ABORT) {
+                               /* Stop parsing the request */
+                               break;
+                       }
+
+                       /* Remove from the buffer */
+                       used = next - buf + 2;
+                       memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+                       buf_fill -= (int)used;
+               }
+
+               /* All parts handled */
+               return field_count;
+       }
+
+       /* Unknown Content-Type */
+       return -1;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/md5.inl b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/md5.inl
new file mode 100644 (file)
index 0000000..733f176
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * This an amalgamation of md5.c and md5.h into a single file
+ * with all static declaration to reduce linker conflicts
+ * in Civetweb.
+ *
+ * The MD5_STATIC declaration was added to facilitate static
+ * inclusion.
+ * No Face Press, LLC
+ */
+
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+    http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+    references to Ghostscript; clarified derivation from RFC 1321;
+    now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+    added conditionalization for C++ compilation from Martin
+    Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t;  /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+       md5_word_t count[2]; /* message length in bits, lsw first */
+       md5_word_t abcd[4];  /* digest buffer */
+       md5_byte_t buf[64];  /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initialize the algorithm. */
+MD5_STATIC void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
+
+/* Finish the message and return the digest. */
+MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
+
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+    http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+    either statically or dynamically; added missing #include <string.h>
+    in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+    type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+    unsigned in ANSI C, signed in traditional"; made test program
+    self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef MD5_STATIC
+#include <string.h>
+#endif
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#define BYTE_ORDER (0)
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 (0x242070db)
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 (0x4787c62a)
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 (0x698098d8)
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 (0x6b901122)
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 (0x49b40821)
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 (0x265e5a51)
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 (0x02441453)
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 (0x21e1cde6)
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 (0x455a14ed)
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 (0x676f02d9)
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 (0x6d9d6122)
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 (0x4bdecfa9)
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 (0x289b7ec6)
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 (0x04881d05)
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 (0x1fa27cf8)
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 (0x432aff97)
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 (0x655b59c3)
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 (0x6fa87e4f)
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 (0x4e0811a1)
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 (0x2ad7d2bb)
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+       md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
+                  d = pms->abcd[3];
+       md5_word_t t;
+#if BYTE_ORDER > 0
+       /* Define storage only for big-endian CPUs. */
+       md5_word_t X[16];
+#else
+       /* Define storage for little-endian or both types of CPUs. */
+       md5_word_t xbuf[16];
+       const md5_word_t *X;
+#endif
+
+       {
+#if BYTE_ORDER == 0
+               /*
+                * Determine dynamically whether this is a big-endian or
+                * little-endian machine, since we can use a more efficient
+                * algorithm on the latter.
+                */
+               static const int w = 1;
+
+               if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+               {
+                       /*
+                        * On little-endian machines, we can process properly aligned
+                        * data without copying it.
+                        */
+                       if (!((data - (const md5_byte_t *)0) & 3)) {
+                               /* data are properly aligned, a direct assignment is possible */
+                               /* cast through a (void *) should avoid a compiler warning,
+                                  see
+                                  https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
+                                  */
+                               X = (const md5_word_t *)(const void *)data;
+                       } else {
+                               /* not aligned */
+                               memcpy(xbuf, data, 64);
+                               X = xbuf;
+                       }
+               }
+#endif
+#if BYTE_ORDER == 0
+               else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+               {
+                       /*
+                        * On big-endian machines, we must arrange the bytes in the
+                        * right order.
+                        */
+                       const md5_byte_t *xp = data;
+                       int i;
+
+#if BYTE_ORDER == 0
+                       X = xbuf; /* (dynamic only) */
+#else
+#define xbuf X /* (static only) */
+#endif
+                       for (i = 0; i < 16; ++i, xp += 4)
+                               xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
+                                         + (md5_word_t)(xp[2] << 16)
+                                         + (md5_word_t)(xp[3] << 24);
+               }
+#endif
+       }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* Round 1. */
+/* Let [abcd k s i] denote the operation
+   a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)                                              \
+       t = a + F(b, c, d) + X[k] + Ti;                                            \
+       a = ROTATE_LEFT(t, s) + b
+
+       /* Do the following 16 operations. */
+       SET(a, b, c, d, 0, 7, T1);
+       SET(d, a, b, c, 1, 12, T2);
+       SET(c, d, a, b, 2, 17, T3);
+       SET(b, c, d, a, 3, 22, T4);
+       SET(a, b, c, d, 4, 7, T5);
+       SET(d, a, b, c, 5, 12, T6);
+       SET(c, d, a, b, 6, 17, T7);
+       SET(b, c, d, a, 7, 22, T8);
+       SET(a, b, c, d, 8, 7, T9);
+       SET(d, a, b, c, 9, 12, T10);
+       SET(c, d, a, b, 10, 17, T11);
+       SET(b, c, d, a, 11, 22, T12);
+       SET(a, b, c, d, 12, 7, T13);
+       SET(d, a, b, c, 13, 12, T14);
+       SET(c, d, a, b, 14, 17, T15);
+       SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+/* Round 2. */
+/* Let [abcd k s i] denote the operation
+     a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)                                              \
+       t = a + G(b, c, d) + X[k] + Ti;                                            \
+       a = ROTATE_LEFT(t, s) + b
+
+       /* Do the following 16 operations. */
+       SET(a, b, c, d, 1, 5, T17);
+       SET(d, a, b, c, 6, 9, T18);
+       SET(c, d, a, b, 11, 14, T19);
+       SET(b, c, d, a, 0, 20, T20);
+       SET(a, b, c, d, 5, 5, T21);
+       SET(d, a, b, c, 10, 9, T22);
+       SET(c, d, a, b, 15, 14, T23);
+       SET(b, c, d, a, 4, 20, T24);
+       SET(a, b, c, d, 9, 5, T25);
+       SET(d, a, b, c, 14, 9, T26);
+       SET(c, d, a, b, 3, 14, T27);
+       SET(b, c, d, a, 8, 20, T28);
+       SET(a, b, c, d, 13, 5, T29);
+       SET(d, a, b, c, 2, 9, T30);
+       SET(c, d, a, b, 7, 14, T31);
+       SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+/* Round 3. */
+/* Let [abcd k s t] denote the operation
+     a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)                                              \
+       t = a + H(b, c, d) + X[k] + Ti;                                            \
+       a = ROTATE_LEFT(t, s) + b
+
+       /* Do the following 16 operations. */
+       SET(a, b, c, d, 5, 4, T33);
+       SET(d, a, b, c, 8, 11, T34);
+       SET(c, d, a, b, 11, 16, T35);
+       SET(b, c, d, a, 14, 23, T36);
+       SET(a, b, c, d, 1, 4, T37);
+       SET(d, a, b, c, 4, 11, T38);
+       SET(c, d, a, b, 7, 16, T39);
+       SET(b, c, d, a, 10, 23, T40);
+       SET(a, b, c, d, 13, 4, T41);
+       SET(d, a, b, c, 0, 11, T42);
+       SET(c, d, a, b, 3, 16, T43);
+       SET(b, c, d, a, 6, 23, T44);
+       SET(a, b, c, d, 9, 4, T45);
+       SET(d, a, b, c, 12, 11, T46);
+       SET(c, d, a, b, 15, 16, T47);
+       SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+/* Round 4. */
+/* Let [abcd k s t] denote the operation
+     a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)                                              \
+       t = a + I(b, c, d) + X[k] + Ti;                                            \
+       a = ROTATE_LEFT(t, s) + b
+
+       /* Do the following 16 operations. */
+       SET(a, b, c, d, 0, 6, T49);
+       SET(d, a, b, c, 7, 10, T50);
+       SET(c, d, a, b, 14, 15, T51);
+       SET(b, c, d, a, 5, 21, T52);
+       SET(a, b, c, d, 12, 6, T53);
+       SET(d, a, b, c, 3, 10, T54);
+       SET(c, d, a, b, 10, 15, T55);
+       SET(b, c, d, a, 1, 21, T56);
+       SET(a, b, c, d, 8, 6, T57);
+       SET(d, a, b, c, 15, 10, T58);
+       SET(c, d, a, b, 6, 15, T59);
+       SET(b, c, d, a, 13, 21, T60);
+       SET(a, b, c, d, 4, 6, T61);
+       SET(d, a, b, c, 11, 10, T62);
+       SET(c, d, a, b, 2, 15, T63);
+       SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+       /* Then perform the following additions. (That is increment each
+          of the four registers by the value it had before this block
+          was started.) */
+       pms->abcd[0] += a;
+       pms->abcd[1] += b;
+       pms->abcd[2] += c;
+       pms->abcd[3] += d;
+}
+
+MD5_STATIC void
+md5_init(md5_state_t *pms)
+{
+       pms->count[0] = pms->count[1] = 0;
+       pms->abcd[0] = 0x67452301;
+       pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+       pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+       pms->abcd[3] = 0x10325476;
+}
+
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
+{
+       const md5_byte_t *p = data;
+       size_t left = nbytes;
+       size_t offset = (pms->count[0] >> 3) & 63;
+       md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+       if (nbytes <= 0)
+               return;
+
+       /* Update the message length. */
+       pms->count[1] += (md5_word_t)(nbytes >> 29);
+       pms->count[0] += nbits;
+       if (pms->count[0] < nbits)
+               pms->count[1]++;
+
+       /* Process an initial partial block. */
+       if (offset) {
+               size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+               memcpy(pms->buf + offset, p, copy);
+               if (offset + copy < 64)
+                       return;
+               p += copy;
+               left -= copy;
+               md5_process(pms, pms->buf);
+       }
+
+       /* Process full blocks. */
+       for (; left >= 64; p += 64, left -= 64)
+               md5_process(pms, p);
+
+       /* Process a final partial block. */
+       if (left)
+               memcpy(pms->buf, p, left);
+}
+
+MD5_STATIC void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+       static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                          0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                          0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                          0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                          0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+       md5_byte_t data[8];
+       int i;
+
+       /* Save the length before padding. */
+       for (i = 0; i < 8; ++i)
+               data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+       /* Pad to 56 bytes mod 64. */
+       md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+       /* Append the length. */
+       md5_append(pms, data, 8);
+       for (i = 0; i < 16; ++i)
+               digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_duktape.inl b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_duktape.inl
new file mode 100644 (file)
index 0000000..5637ae9
--- /dev/null
@@ -0,0 +1,250 @@
+/* This file is part of the CivetWeb web server.
+ * See https://github.com/civetweb/civetweb/
+ * (C) 2015 by the CivetWeb authors, MIT license.
+ */
+
+#include "duktape.h"
+
+/* TODO: the mg context should be added to duktape as well */
+/* Alternative: redefine a new, clean API from scratch (instead of using mg),
+ * or at least do not add problematic functions. */
+/* For evaluation purposes, currently only "send" is supported.
+ * All other ~50 functions will be added later. */
+
+/* Note: This is only experimental support, so the API may still change. */
+
+static const char *civetweb_conn_id = "\xFF"
+                                      "civetweb_conn";
+static const char *civetweb_ctx_id = "\xFF"
+                                     "civetweb_ctx";
+
+
+static void *
+mg_duk_mem_alloc(void *udata, duk_size_t size)
+{
+       return mg_malloc(size);
+}
+
+
+static void *
+mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
+{
+       return mg_realloc(ptr, newsize);
+}
+
+
+static void
+mg_duk_mem_free(void *udata, void *ptr)
+{
+       mg_free(ptr);
+}
+
+
+static void
+mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
+{
+       /* Script is called "protected" (duk_peval_file), so script errors should
+        * never yield in a call to this function. Maybe calls prior to executing
+        * the script could raise a fatal error. */
+       struct mg_connection *conn;
+
+       duk_push_global_stash(ctx);
+       duk_get_prop_string(ctx, -1, civetweb_conn_id);
+       conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+       mg_cry(conn, "%s", msg);
+}
+
+
+static duk_ret_t
+duk_itf_write(duk_context *ctx)
+{
+       struct mg_connection *conn;
+       duk_double_t ret;
+       duk_size_t len = 0;
+       const char *val = duk_require_lstring(ctx, -1, &len);
+
+       /*
+           duk_push_global_stash(ctx);
+           duk_get_prop_string(ctx, -1, civetweb_conn_id);
+           conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+       */
+       duk_push_current_function(ctx);
+       duk_get_prop_string(ctx, -1, civetweb_conn_id);
+       conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+       if (!conn) {
+               duk_error(ctx,
+                         DUK_ERR_INTERNAL_ERROR,
+                         "function not available without connection object");
+               /* probably never reached, but satisfies static code analysis */
+               return DUK_RET_INTERNAL_ERROR;
+       }
+
+       ret = mg_write(conn, val, len);
+
+       duk_push_number(ctx, ret);
+       return 1;
+}
+
+
+static duk_ret_t
+duk_itf_read(duk_context *ctx)
+{
+       struct mg_connection *conn;
+       char buf[1024];
+       int len;
+
+       duk_push_global_stash(ctx);
+       duk_get_prop_string(ctx, -1, civetweb_conn_id);
+       conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
+
+       if (!conn) {
+               duk_error(ctx,
+                         DUK_ERR_INTERNAL_ERROR,
+                         "function not available without connection object");
+               /* probably never reached, but satisfies static code analysis */
+               return DUK_RET_INTERNAL_ERROR;
+       }
+
+       len = mg_read(conn, buf, sizeof(buf));
+
+       duk_push_lstring(ctx, buf, len);
+       return 1;
+}
+
+
+static duk_ret_t
+duk_itf_getoption(duk_context *ctx)
+{
+       struct mg_context *cv_ctx;
+       const char *ret;
+       duk_size_t len = 0;
+       const char *val = duk_require_lstring(ctx, -1, &len);
+
+       duk_push_current_function(ctx);
+       duk_get_prop_string(ctx, -1, civetweb_ctx_id);
+       cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
+
+       if (!cv_ctx) {
+               duk_error(ctx,
+                         DUK_ERR_INTERNAL_ERROR,
+                         "function not available without connection object");
+               /* probably never reached, but satisfies static code analysis */
+               return DUK_RET_INTERNAL_ERROR;
+       }
+
+       ret = mg_get_option(cv_ctx, val);
+       if (ret) {
+               duk_push_string(ctx, ret);
+       } else {
+               duk_push_null(ctx);
+       }
+
+       return 1;
+}
+
+
+static void
+mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
+{
+       int i;
+       duk_context *ctx = NULL;
+
+       conn->must_close = 1;
+
+       /* Create Duktape interpreter state */
+       ctx = duk_create_heap(mg_duk_mem_alloc,
+                             mg_duk_mem_realloc,
+                             mg_duk_mem_free,
+                             NULL,
+                             mg_duk_fatal_handler);
+       if (!ctx) {
+               mg_cry(conn, "Failed to create a Duktape heap.");
+               goto exec_duktape_finished;
+       }
+
+       /* Add "conn" object */
+       duk_push_global_object(ctx);
+       duk_push_object(ctx); /* create a new table/object ("conn") */
+
+       duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
+       duk_push_pointer(ctx, (void *)conn);
+       duk_put_prop_string(ctx, -2, civetweb_conn_id);
+       duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
+
+       duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
+       duk_push_pointer(ctx, (void *)conn);
+       duk_put_prop_string(ctx, -2, civetweb_conn_id);
+       duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
+
+       duk_push_string(ctx, conn->request_info.request_method);
+       duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
+
+       duk_push_string(ctx, conn->request_info.request_uri);
+       duk_put_prop_string(ctx, -2, "request_uri");
+
+       duk_push_string(ctx, conn->request_info.local_uri);
+       duk_put_prop_string(ctx, -2, "uri");
+
+       duk_push_string(ctx, conn->request_info.http_version);
+       duk_put_prop_string(ctx, -2, "http_version");
+
+       duk_push_string(ctx, conn->request_info.query_string);
+       duk_put_prop_string(ctx, -2, "query_string");
+
+       duk_push_string(ctx, conn->request_info.remote_addr);
+       duk_put_prop_string(ctx, -2, "remote_addr");
+
+       duk_push_int(ctx, conn->request_info.remote_port);
+       duk_put_prop_string(ctx, -2, "remote_port");
+
+       duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
+       duk_put_prop_string(ctx, -2, "server_port");
+
+       duk_push_object(ctx); /* subfolder "conn.http_headers" */
+       for (i = 0; i < conn->request_info.num_headers; i++) {
+               duk_push_string(ctx, conn->request_info.http_headers[i].value);
+               duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
+       }
+       duk_put_prop_string(ctx, -2, "http_headers");
+
+       duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
+
+       /* Add "civetweb" object */
+       duk_push_global_object(ctx);
+       duk_push_object(ctx); /* create a new table/object ("conn") */
+
+       duk_push_string(ctx, CIVETWEB_VERSION);
+       duk_put_prop_string(ctx, -2, "version");
+
+       duk_push_string(ctx, script_name);
+       duk_put_prop_string(ctx, -2, "script_name");
+
+       if (conn->ctx != NULL) {
+               duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
+               duk_push_pointer(ctx, (void *)(conn->ctx));
+               duk_put_prop_string(ctx, -2, civetweb_ctx_id);
+               duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
+
+               if (conn->ctx->systemName != NULL) {
+                       duk_push_string(ctx, conn->ctx->systemName);
+                       duk_put_prop_string(ctx, -2, "system");
+               }
+       }
+
+       duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
+
+       duk_push_global_stash(ctx);
+       duk_push_pointer(ctx, (void *)conn);
+       duk_put_prop_string(ctx, -2, civetweb_conn_id);
+
+       if (duk_peval_file(ctx, script_name) != 0) {
+               mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
+               goto exec_duktape_finished;
+       }
+       duk_pop(ctx); /* ignore result */
+
+exec_duktape_finished:
+       duk_destroy_heap(ctx);
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_lua.inl b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/mod_lua.inl
new file mode 100644 (file)
index 0000000..8d7d425
--- /dev/null
@@ -0,0 +1,1840 @@
+#include "civetweb_lua.h"
+#include "civetweb_private_lua.h"
+
+#ifdef _WIN32
+static void *
+mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
+{
+       /* TODO (low): This is an incomplete implementation of mmap for windows.
+        * Currently it is sufficient, but there are a lot of unused parameters.
+        * Better use a function "mg_map" which only has the required parameters,
+        * and implement it using mmap in Linux and CreateFileMapping in Windows.
+        * Noone should expect a full mmap for Windows here.
+        */
+       HANDLE fh = (HANDLE)_get_osfhandle(fd);
+       HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
+       void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t)len);
+       CloseHandle(mh);
+
+       /* unused parameters */
+       (void)addr;
+       (void)prot;
+       (void)flags;
+       (void)offset;
+
+       return p;
+}
+
+static void
+munmap(void *addr, int64_t length)
+{
+       /* unused parameters */
+       (void)length;
+
+       UnmapViewOfFile(addr);
+}
+
+#define MAP_FAILED (NULL)
+#define MAP_PRIVATE (0)
+#define PROT_READ (0)
+#else
+#include <sys/mman.h>
+#endif
+
+static const char *LUASOCKET = "luasocket";
+static const char lua_regkey_ctx = 1;
+static const char lua_regkey_connlist = 2;
+
+/* Forward declarations */
+static void handle_request(struct mg_connection *);
+static int handle_lsp_request(struct mg_connection *,
+                              const char *,
+                              struct file *,
+                              struct lua_State *);
+
+static void
+reg_string(struct lua_State *L, const char *name, const char *val)
+{
+       if (name != NULL && val != NULL) {
+               lua_pushstring(L, name);
+               lua_pushstring(L, val);
+               lua_rawset(L, -3);
+       }
+}
+
+static void
+reg_int(struct lua_State *L, const char *name, int val)
+{
+       if (name != NULL) {
+               lua_pushstring(L, name);
+               lua_pushinteger(L, val);
+               lua_rawset(L, -3);
+       }
+}
+
+static void
+reg_boolean(struct lua_State *L, const char *name, int val)
+{
+       if (name != NULL) {
+               lua_pushstring(L, name);
+               lua_pushboolean(L, val != 0);
+               lua_rawset(L, -3);
+       }
+}
+
+static void
+reg_conn_function(struct lua_State *L,
+                  const char *name,
+                  lua_CFunction func,
+                  struct mg_connection *conn)
+{
+       if (name != NULL && func != NULL && conn != NULL) {
+               lua_pushstring(L, name);
+               lua_pushlightuserdata(L, conn);
+               lua_pushcclosure(L, func, 1);
+               lua_rawset(L, -3);
+       }
+}
+
+static void
+reg_function(struct lua_State *L, const char *name, lua_CFunction func)
+{
+       if (name != NULL && func != NULL) {
+               lua_pushstring(L, name);
+               lua_pushcclosure(L, func, 0);
+               lua_rawset(L, -3);
+       }
+}
+
+static void
+lua_cry(struct mg_connection *conn,
+        int err,
+        lua_State *L,
+        const char *lua_title,
+        const char *lua_operation)
+{
+       switch (err) {
+       case LUA_OK:
+       case LUA_YIELD:
+               break;
+       case LUA_ERRRUN:
+               mg_cry(conn,
+                      "%s: %s failed: runtime error: %s",
+                      lua_title,
+                      lua_operation,
+                      lua_tostring(L, -1));
+               break;
+       case LUA_ERRSYNTAX:
+               mg_cry(conn,
+                      "%s: %s failed: syntax error: %s",
+                      lua_title,
+                      lua_operation,
+                      lua_tostring(L, -1));
+               break;
+       case LUA_ERRMEM:
+               mg_cry(conn, "%s: %s failed: out of memory", lua_title, lua_operation);
+               break;
+       case LUA_ERRGCMM:
+               mg_cry(conn,
+                      "%s: %s failed: error during garbage collection",
+                      lua_title,
+                      lua_operation);
+               break;
+       case LUA_ERRERR:
+               mg_cry(conn,
+                      "%s: %s failed: error in error handling: %s",
+                      lua_title,
+                      lua_operation,
+                      lua_tostring(L, -1));
+               break;
+       default:
+               mg_cry(conn, "%s: %s failed: error %i", lua_title, lua_operation, err);
+               break;
+       }
+}
+
+static int
+lsp_sock_close(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       if ((num_args == 1) && lua_istable(L, -1)) {
+               lua_getfield(L, -1, "sock");
+               closesocket((SOCKET)lua_tonumber(L, -1));
+       } else {
+               return luaL_error(L, "invalid :close() call");
+       }
+       return 1;
+}
+
+static int
+lsp_sock_recv(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       char buf[2000];
+       int n;
+
+       if ((num_args == 1) && lua_istable(L, -1)) {
+               lua_getfield(L, -1, "sock");
+               n = recv((SOCKET)lua_tonumber(L, -1), buf, sizeof(buf), 0);
+               if (n <= 0) {
+                       lua_pushnil(L);
+               } else {
+                       lua_pushlstring(L, buf, n);
+               }
+       } else {
+               return luaL_error(L, "invalid :close() call");
+       }
+       return 1;
+}
+
+static int
+lsp_sock_send(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *buf;
+       size_t len, sent = 0;
+       int n = 0, sock;
+
+       if ((num_args == 2) && lua_istable(L, -2) && lua_isstring(L, -1)) {
+               buf = lua_tolstring(L, -1, &len);
+               lua_getfield(L, -2, "sock");
+               sock = (int)lua_tonumber(L, -1);
+               while (sent < len) {
+                       if ((n = send(sock, buf + sent, (int)(len - sent), 0)) <= 0) {
+                               break;
+                       }
+                       sent += n;
+               }
+               lua_pushnumber(L, n);
+       } else {
+               return luaL_error(L, "invalid :close() call");
+       }
+       return 1;
+}
+
+static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
+                                                    {"send", lsp_sock_send},
+                                                    {"recv", lsp_sock_recv},
+                                                    {NULL, NULL}};
+
+static int
+lsp_connect(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       char ebuf[100];
+       SOCKET sock;
+       union usa sa;
+       int ok;
+
+       if ((num_args == 3) && lua_isstring(L, -3) && lua_isnumber(L, -2)
+           && lua_isnumber(L, -1)) {
+               ok = connect_socket(NULL,
+                                   lua_tostring(L, -3),
+                                   (int)lua_tonumber(L, -2),
+                                   (int)lua_tonumber(L, -1),
+                                   ebuf,
+                                   sizeof(ebuf),
+                                   &sock,
+                                   &sa);
+               if (!ok) {
+                       return luaL_error(L, ebuf);
+               } else {
+                       lua_newtable(L);
+                       reg_int(L, "sock", (int)sock);
+                       reg_string(L, "host", lua_tostring(L, -4));
+                       luaL_getmetatable(L, LUASOCKET);
+                       lua_setmetatable(L, -2);
+                       /* TODO (high): The metatable misses a _gc method to free the
+                        * sock object -> currently lsp_connect is a resource leak. */
+               }
+       } else {
+               return luaL_error(
+                   L, "connect(host,port,is_ssl): invalid parameter given.");
+       }
+       return 1;
+}
+
+static int
+lsp_error(lua_State *L)
+{
+       lua_getglobal(L, "mg");
+       lua_getfield(L, -1, "onerror");
+       lua_pushvalue(L, -3);
+       lua_pcall(L, 1, 0, 0);
+       return 0;
+}
+
+/* Silently stop processing chunks. */
+static void
+lsp_abort(lua_State *L)
+{
+       int top = lua_gettop(L);
+       lua_getglobal(L, "mg");
+       lua_pushnil(L);
+       lua_setfield(L, -2, "onerror");
+       lua_settop(L, top);
+       lua_pushstring(L, "aborting");
+       lua_error(L);
+}
+
+struct lsp_var_reader_data {
+       const char *begin;
+       unsigned len;
+       unsigned state;
+};
+
+
+static const char *
+lsp_var_reader(lua_State *L, void *ud, size_t *sz)
+{
+       struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud;
+       const char *ret;
+       (void)(L); /* unused */
+
+       switch (reader->state) {
+       case 0:
+               ret = "mg.write(";
+               *sz = strlen(ret);
+               break;
+       case 1:
+               ret = reader->begin;
+               *sz = reader->len;
+               break;
+       case 2:
+               ret = ")";
+               *sz = strlen(ret);
+               break;
+       default:
+               ret = 0;
+               *sz = 0;
+       }
+
+       reader->state++;
+       return ret;
+}
+
+
+static int
+lsp(struct mg_connection *conn,
+    const char *path,
+    const char *p,
+    int64_t len,
+    lua_State *L)
+{
+       int i, j, pos = 0, lines = 1, lualines = 0, is_var, lua_ok;
+       char chunkname[MG_BUF_LEN];
+       struct lsp_var_reader_data data;
+
+       for (i = 0; i < len; i++) {
+               if (p[i] == '\n')
+                       lines++;
+               if ((i + 1) < len && p[i] == '<' && p[i + 1] == '?') {
+
+                       /* <?= ?> means a variable is enclosed and its value should be
+                        * printed */
+                       is_var = ((i + 2) < len && p[i + 2] == '=');
+
+                       if (is_var)
+                               j = i + 2;
+                       else
+                               j = i + 1;
+
+                       while (j < len) {
+                               if (p[j] == '\n')
+                                       lualines++;
+                               if ((j + 1) < len && p[j] == '?' && p[j + 1] == '>') {
+                                       mg_write(conn, p + pos, i - pos);
+
+                                       mg_snprintf(conn,
+                                                   NULL, /* name only used for debugging */
+                                                   chunkname,
+                                                   sizeof(chunkname),
+                                                   "@%s+%i",
+                                                   path,
+                                                   lines);
+                                       lua_pushlightuserdata(L, conn);
+                                       lua_pushcclosure(L, lsp_error, 1);
+
+                                       if (is_var) {
+                                               data.begin = p + (i + 3);
+                                               data.len = j - (i + 3);
+                                               data.state = 0;
+                                               lua_ok = mg_lua_load(
+                                                   L, lsp_var_reader, &data, chunkname, NULL);
+                                       } else {
+                                               lua_ok = luaL_loadbuffer(L,
+                                                                        p + (i + 2),
+                                                                        j - (i + 2),
+                                                                        chunkname);
+                                       }
+
+                                       if (lua_ok) {
+                                               /* Syntax error or OOM. Error message is pushed on
+                                                * stack. */
+                                               lua_pcall(L, 1, 0, 0);
+                                       } else {
+                                               /* Success loading chunk. Call it. */
+                                               lua_pcall(L, 0, 0, 1);
+                                       }
+
+                                       pos = j + 2;
+                                       i = pos - 1;
+                                       break;
+                               }
+                               j++;
+                       }
+
+                       if (lualines > 0) {
+                               lines += lualines;
+                               lualines = 0;
+                       }
+               }
+       }
+
+       if (i > pos) {
+               mg_write(conn, p + pos, i - pos);
+       }
+
+       return 0;
+}
+
+
+/* mg.write: Send data to the client */
+static int
+lsp_write(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+       const char *str;
+       size_t size;
+       int i;
+
+       for (i = 1; i <= num_args; i++) {
+               if (lua_isstring(L, i)) {
+                       str = lua_tolstring(L, i, &size);
+                       mg_write(conn, str, size);
+               }
+       }
+
+       return 0;
+}
+
+
+/* mg.read: Read data from the client (e.g., from a POST request) */
+static int
+lsp_read(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       char buf[1024];
+       int len = mg_read(conn, buf, sizeof(buf));
+
+       if (len <= 0)
+               return 0;
+       lua_pushlstring(L, buf, len);
+
+       return 1;
+}
+
+
+/* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
+static int
+lsp_keep_alive(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+
+       /* This function may be called with one parameter (boolean) to set the
+       keep_alive state.
+       Or without a parameter to just query the current keep_alive state. */
+       if ((num_args == 1) && lua_isboolean(L, 1)) {
+               conn->must_close = !lua_toboolean(L, 1);
+       } else if (num_args != 0) {
+               /* Syntax error */
+               return luaL_error(L, "invalid keep_alive() call");
+       }
+
+       /* Return the current "keep_alive" state. This may be false, even it
+        * keep_alive(true) has been called. */
+       lua_pushboolean(L, should_keep_alive(conn));
+       return 1;
+}
+
+
+/* mg.include: Include another .lp file */
+static int
+lsp_include(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+       struct file file = STRUCT_FILE_INITIALIZER;
+       const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+       if (filename) {
+               if (handle_lsp_request(conn, filename, &file, L)) {
+                       /* handle_lsp_request returned an error code, meaning an error
+                       occured in
+                       the included page and mg.onerror returned non-zero. Stop processing.
+                       */
+                       lsp_abort(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid include() call");
+       }
+       return 0;
+}
+
+
+/* mg.cry: Log an error. Default value for mg.onerror. */
+static int
+lsp_cry(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+       const char *text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+       if (text) {
+               mg_cry(conn, "%s", lua_tostring(L, -1));
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid cry() call");
+       }
+       return 0;
+}
+
+
+/* mg.redirect: Redirect the request (internally). */
+static int
+lsp_redirect(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+       const char *target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+       if (target) {
+               conn->request_info.local_uri = target;
+               handle_request(conn);
+               lsp_abort(L);
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid redirect() call");
+       }
+       return 0;
+}
+
+
+/* mg.send_file */
+static int
+lsp_send_file(lua_State *L)
+{
+       struct mg_connection *conn =
+           (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+       int num_args = lua_gettop(L);
+       const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
+
+       if (filename) {
+               mg_send_file(conn, filename);
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid send_file() call");
+       }
+       return 0;
+}
+
+
+/* mg.get_time */
+static int
+lsp_get_time(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0;
+       struct timespec ts;
+       double d;
+
+       clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts);
+       d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9);
+       lua_pushnumber(L, d);
+       return 1;
+}
+
+
+/* mg.get_var */
+static int
+lsp_get_var(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *data, *var_name;
+       size_t data_len, occurrence;
+       int ret;
+       char dst[512];
+
+       if (num_args >= 2 && num_args <= 3) {
+               data = lua_tolstring(L, 1, &data_len);
+               var_name = lua_tostring(L, 2);
+               occurrence = (num_args > 2) ? (long)lua_tonumber(L, 3) : 0;
+
+               ret =
+                   mg_get_var2(data, data_len, var_name, dst, sizeof(dst), occurrence);
+               if (ret >= 0) {
+                       /* Variable found: return value to Lua */
+                       lua_pushstring(L, dst);
+               } else {
+                       /* Variable not found (TODO (mid): may be string too long) */
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid get_var() call");
+       }
+       return 1;
+}
+
+
+/* mg.get_mime_type */
+static int
+lsp_get_mime_type(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       struct vec mime_type = {0, 0};
+       struct mg_context *ctx;
+       const char *text;
+
+       lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+       lua_gettable(L, LUA_REGISTRYINDEX);
+       ctx = (struct mg_context *)lua_touserdata(L, -1);
+
+       if (num_args == 1) {
+               text = lua_tostring(L, 1);
+               if (text) {
+                       if (ctx) {
+                               get_mime_type(ctx, text, &mime_type);
+                               lua_pushlstring(L, mime_type.ptr, mime_type.len);
+                       } else {
+                               text = mg_get_builtin_mime_type(text);
+                               lua_pushstring(L, text);
+                       }
+               } else {
+                       /* Syntax error */
+                       return luaL_error(L, "invalid argument for get_mime_type() call");
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid get_mime_type() call");
+       }
+       return 1;
+}
+
+
+/* mg.get_cookie */
+static int
+lsp_get_cookie(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *cookie;
+       const char *var_name;
+       int ret;
+       char dst[512];
+
+       if (num_args == 2) {
+               cookie = lua_tostring(L, 1);
+               var_name = lua_tostring(L, 2);
+               if (cookie != NULL && var_name != NULL) {
+                       ret = mg_get_cookie(cookie, var_name, dst, sizeof(dst));
+               } else {
+                       ret = -1;
+               }
+
+               if (ret >= 0) {
+                       lua_pushlstring(L, dst, ret);
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid get_cookie() call");
+       }
+       return 1;
+}
+
+
+/* mg.md5 */
+static int
+lsp_md5(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *text;
+       md5_byte_t hash[16];
+       md5_state_t ctx;
+       size_t text_len;
+       char buf[40];
+
+       if (num_args == 1) {
+               text = lua_tolstring(L, 1, &text_len);
+               if (text) {
+                       md5_init(&ctx);
+                       md5_append(&ctx, (const md5_byte_t *)text, text_len);
+                       md5_finish(&ctx, hash);
+                       bin2str(buf, hash, sizeof(hash));
+                       lua_pushstring(L, buf);
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid md5() call");
+       }
+       return 1;
+}
+
+
+/* mg.url_encode */
+static int
+lsp_url_encode(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *text;
+       size_t text_len;
+       char dst[512];
+
+       if (num_args == 1) {
+               text = lua_tolstring(L, 1, &text_len);
+               if (text) {
+                       mg_url_encode(text, dst, sizeof(dst));
+                       lua_pushstring(L, dst);
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid url_encode() call");
+       }
+       return 1;
+}
+
+
+/* mg.url_decode */
+static int
+lsp_url_decode(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *text;
+       size_t text_len;
+       int is_form;
+       char dst[512];
+
+       if (num_args == 1 || (num_args == 2 && lua_isboolean(L, 2))) {
+               text = lua_tolstring(L, 1, &text_len);
+               is_form = (num_args == 2) ? lua_isboolean(L, 2) : 0;
+               if (text) {
+                       mg_url_decode(text, text_len, dst, (int)sizeof(dst), is_form);
+                       lua_pushstring(L, dst);
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid url_decode() call");
+       }
+       return 1;
+}
+
+
+/* mg.base64_encode */
+static int
+lsp_base64_encode(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *text;
+       size_t text_len;
+       char *dst;
+
+       if (num_args == 1) {
+               text = lua_tolstring(L, 1, &text_len);
+               if (text) {
+                       dst = (char *)mg_malloc(text_len * 8 / 6 + 4);
+                       if (dst) {
+                               base64_encode((const unsigned char *)text, (int)text_len, dst);
+                               lua_pushstring(L, dst);
+                               mg_free(dst);
+                       } else {
+                               return luaL_error(L, "out of memory in base64_encode() call");
+                       }
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid base64_encode() call");
+       }
+       return 1;
+}
+
+
+/* mg.base64_encode */
+static int
+lsp_base64_decode(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       const char *text;
+       size_t text_len, dst_len;
+       int ret;
+       char *dst;
+
+       if (num_args == 1) {
+               text = lua_tolstring(L, 1, &text_len);
+               if (text) {
+                       dst = (char *)mg_malloc(text_len);
+                       if (dst) {
+                               ret = base64_decode((const unsigned char *)text,
+                                                   (int)text_len,
+                                                   dst,
+                                                   &dst_len);
+                               if (ret != -1) {
+                                       mg_free(dst);
+                                       return luaL_error(
+                                           L, "illegal character in lsp_base64_decode() call");
+                               } else {
+                                       lua_pushlstring(L, dst, dst_len);
+                                       mg_free(dst);
+                               }
+                       } else {
+                               return luaL_error(L,
+                                                 "out of memory in lsp_base64_decode() call");
+                       }
+               } else {
+                       lua_pushnil(L);
+               }
+       } else {
+               /* Syntax error */
+               return luaL_error(L, "invalid lsp_base64_decode() call");
+       }
+       return 1;
+}
+
+
+/* mg.get_response_code_text */
+static int
+lsp_get_response_code_text(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       int type1;
+       double code;
+       const char *text;
+
+       if (num_args == 1) {
+               type1 = lua_type(L, 1);
+               if (type1 == LUA_TNUMBER) {
+                       /* If the first argument is a number,
+                          convert it to the corresponding text. */
+                       code = lua_tonumber(L, 1);
+                       text = mg_get_response_code_text(NULL, (int)code);
+                       if (text)
+                               lua_pushstring(L, text);
+                       return text ? 1 : 0;
+               }
+       }
+
+       /* Syntax error */
+       return luaL_error(L, "invalid get_response_code_text() call");
+}
+
+
+/* mg.random - might be better than math.random on some systems */
+static int
+lsp_random(lua_State *L)
+{
+       int num_args = lua_gettop(L);
+       if (num_args == 0) {
+               /* The civetweb internal random number generator will generate
+                        * a 64 bit random number. */
+               uint64_t r = get_random();
+               /* Lua "number" is a IEEE 754 double precission float:
+ * https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+                * Thus, mask with 2^53-1 to get an integer with the maximum
+ * precission available. */
+               r &= ((((uint64_t)1) << 53) - 1);
+               lua_pushnumber(L, (double)r);
+               return 1;
+       }
+
+       /* Syntax error */
+       return luaL_error(L, "invalid random() call");
+}
+
+
+union {
+       void *p;
+       void (*f)(unsigned char uuid[16]);
+} pf_uuid_generate;
+
+
+/* mg.uuid */
+static int
+lsp_uuid(lua_State *L)
+{
+       union {
+               unsigned char uuid_array[16];
+               struct uuid_struct_type {
+                       uint32_t data1;
+                       uint16_t data2;
+                       uint16_t data3;
+                       uint8_t data4[8];
+               } uuid_struct;
+       } uuid;
+
+       char uuid_str[40];
+       int num_args = lua_gettop(L);
+
+       memset(&uuid, 0, sizeof(uuid));
+       memset(uuid_str, 0, sizeof(uuid_str));
+
+       if (num_args == 0) {
+
+               pf_uuid_generate.f(uuid.uuid_array);
+
+               sprintf(uuid_str,
+                       "{%08lX-%04X-%04X-%02X%02X-"
+                       "%02X%02X%02X%02X%02X%02X}",
+                       (unsigned long)uuid.uuid_struct.data1,
+                       (unsigned)uuid.uuid_struct.data2,
+                       (unsigned)uuid.uuid_struct.data3,
+                       (unsigned)uuid.uuid_struct.data4[0],
+                       (unsigned)uuid.uuid_struct.data4[1],
+                       (unsigned)uuid.uuid_struct.data4[2],
+                       (unsigned)uuid.uuid_struct.data4[3],
+                       (unsigned)uuid.uuid_struct.data4[4],
+                       (unsigned)uuid.uuid_struct.data4[5],
+                       (unsigned)uuid.uuid_struct.data4[6],
+                       (unsigned)uuid.uuid_struct.data4[7]);
+
+               lua_pushstring(L, uuid_str);
+               return 1;
+       }
+
+       /* Syntax error */
+       return luaL_error(L, "invalid random() call");
+}
+
+
+#ifdef USE_WEBSOCKET
+struct lua_websock_data {
+       lua_State *state;
+       char *script;
+       unsigned references;
+       struct mg_connection *conn[MAX_WORKER_THREADS];
+       pthread_mutex_t ws_mutex;
+};
+#endif
+
+
+/* mg.write for websockets */
+static int
+lwebsock_write(lua_State *L)
+{
+#ifdef USE_WEBSOCKET
+       int num_args = lua_gettop(L);
+       struct lua_websock_data *ws;
+       const char *str;
+       size_t size;
+       int opcode = -1;
+       unsigned i;
+       struct mg_connection *client = NULL;
+
+       lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+       lua_gettable(L, LUA_REGISTRYINDEX);
+       ws = (struct lua_websock_data *)lua_touserdata(L, -1);
+
+       (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+       if (num_args == 1) {
+               /* just one text: send it to all client */
+               if (lua_isstring(L, 1)) {
+                       opcode = WEBSOCKET_OPCODE_TEXT;
+               }
+       } else if (num_args == 2) {
+               if (lua_isnumber(L, 1)) {
+                       /* opcode number and message text */
+                       opcode = (int)lua_tointeger(L, 1);
+               } else if (lua_isstring(L, 1)) {
+                       /* opcode string and message text */
+                       str = lua_tostring(L, 1);
+                       if (!mg_strncasecmp(str, "text", 4))
+                               opcode = WEBSOCKET_OPCODE_TEXT;
+                       else if (!mg_strncasecmp(str, "bin", 3))
+                               opcode = WEBSOCKET_OPCODE_BINARY;
+                       else if (!mg_strncasecmp(str, "close", 5))
+                               opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
+                       else if (!mg_strncasecmp(str, "ping", 4))
+                               opcode = WEBSOCKET_OPCODE_PING;
+                       else if (!mg_strncasecmp(str, "pong", 4))
+                               opcode = WEBSOCKET_OPCODE_PONG;
+                       else if (!mg_strncasecmp(str, "cont", 4))
+                               opcode = WEBSOCKET_OPCODE_CONTINUATION;
+               } else if (lua_isuserdata(L, 1)) {
+                       /* client id and message text */
+                       client = (struct mg_connection *)lua_touserdata(L, 1);
+                       opcode = WEBSOCKET_OPCODE_TEXT;
+               }
+       } else if (num_args == 3) {
+               if (lua_isuserdata(L, 1)) {
+                       client = (struct mg_connection *)lua_touserdata(L, 1);
+                       if (lua_isnumber(L, 2)) {
+                               /* client id, opcode number and message text */
+                               opcode = (int)lua_tointeger(L, 2);
+                       } else if (lua_isstring(L, 2)) {
+                               /* client id, opcode string and message text */
+                               str = lua_tostring(L, 2);
+                               if (!mg_strncasecmp(str, "text", 4))
+                                       opcode = WEBSOCKET_OPCODE_TEXT;
+                               else if (!mg_strncasecmp(str, "bin", 3))
+                                       opcode = WEBSOCKET_OPCODE_BINARY;
+                               else if (!mg_strncasecmp(str, "close", 5))
+                                       opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
+                               else if (!mg_strncasecmp(str, "ping", 4))
+                                       opcode = WEBSOCKET_OPCODE_PING;
+                               else if (!mg_strncasecmp(str, "pong", 4))
+                                       opcode = WEBSOCKET_OPCODE_PONG;
+                               else if (!mg_strncasecmp(str, "cont", 4))
+                                       opcode = WEBSOCKET_OPCODE_CONTINUATION;
+                       }
+               }
+       }
+
+       if (opcode >= 0 && opcode < 16 && lua_isstring(L, num_args)) {
+               str = lua_tolstring(L, num_args, &size);
+               if (client) {
+                       for (i = 0; i < ws->references; i++) {
+                               if (client == ws->conn[i]) {
+                                       mg_websocket_write(ws->conn[i], opcode, str, size);
+                               }
+                       }
+               } else {
+                       for (i = 0; i < ws->references; i++) {
+                               mg_websocket_write(ws->conn[i], opcode, str, size);
+                       }
+               }
+       } else {
+               (void)pthread_mutex_unlock(&(ws->ws_mutex));
+               return luaL_error(L, "invalid websocket write() call");
+       }
+
+       (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+#else
+       (void)(L);           /* unused */
+#endif
+       return 0;
+}
+
+
+struct laction_arg {
+       lua_State *state;
+       const char *script;
+       pthread_mutex_t *pmutex;
+       char txt[1];
+};
+
+
+static int
+lua_action(struct laction_arg *arg)
+{
+       int err, ok;
+       struct mg_context *ctx;
+
+       (void)pthread_mutex_lock(arg->pmutex);
+
+       lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx);
+       lua_gettable(arg->state, LUA_REGISTRYINDEX);
+       ctx = (struct mg_context *)lua_touserdata(arg->state, -1);
+
+       err = luaL_loadstring(arg->state, arg->txt);
+       if (err != 0) {
+               lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
+               (void)pthread_mutex_unlock(arg->pmutex);
+               mg_free(arg);
+               return 0;
+       }
+       err = lua_pcall(arg->state, 0, 1, 0);
+       if (err != 0) {
+               lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
+               (void)pthread_mutex_unlock(arg->pmutex);
+               mg_free(arg);
+               return 0;
+       }
+
+       ok = lua_type(arg->state, -1);
+       if (lua_isboolean(arg->state, -1)) {
+               ok = lua_toboolean(arg->state, -1);
+       } else {
+               ok = 0;
+       }
+       lua_pop(arg->state, 1);
+
+       (void)pthread_mutex_unlock(arg->pmutex);
+
+       if (!ok) {
+               mg_free(arg);
+       }
+       return ok;
+}
+
+
+static int
+lua_action_free(struct laction_arg *arg)
+{
+       if (lua_action(arg)) {
+               mg_free(arg);
+       }
+       return 0;
+}
+
+
+static int
+lwebsocket_set_timer(lua_State *L, int is_periodic)
+{
+#if defined(USE_TIMERS) && defined(USE_WEBSOCKET)
+       int num_args = lua_gettop(L);
+       struct lua_websock_data *ws;
+       int type1, type2, ok = 0;
+       double timediff;
+       struct mg_context *ctx;
+       struct laction_arg *arg;
+       const char *txt;
+       size_t txt_len;
+
+       lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+       lua_gettable(L, LUA_REGISTRYINDEX);
+       ctx = (struct mg_context *)lua_touserdata(L, -1);
+
+       lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+       lua_gettable(L, LUA_REGISTRYINDEX);
+       ws = (struct lua_websock_data *)lua_touserdata(L, -1);
+
+       if (num_args < 2) {
+               return luaL_error(L,
+                                 "not enough arguments for set_timer/interval() call");
+       }
+
+       type1 = lua_type(L, 1);
+       type2 = lua_type(L, 2);
+
+       if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) {
+               timediff = (double)lua_tonumber(L, 2);
+               txt = lua_tostring(L, 1);
+               txt_len = strlen(txt);
+               arg = (struct laction_arg *)mg_malloc(sizeof(struct laction_arg)
+                                                     + txt_len + 10);
+               arg->state = L;
+               arg->script = ws->script;
+               arg->pmutex = &(ws->ws_mutex);
+               memcpy(arg->txt, "return(", 7);
+               memcpy(arg->txt + 7, txt, txt_len);
+               arg->txt[txt_len + 7] = ')';
+               arg->txt[txt_len + 8] = 0;
+               ok =
+                   (0
+                    == timer_add(ctx,
+                                 timediff,
+                                 is_periodic,
+                                 1,
+                                 (taction)(is_periodic ? lua_action : lua_action_free),
+                                 (void *)arg));
+       } else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) {
+               /* TODO (mid): not implemented yet */
+               return luaL_error(L, "invalid arguments for set_timer/interval() call");
+       } else {
+               return luaL_error(L, "invalid arguments for set_timer/interval() call");
+       }
+
+       lua_pushboolean(L, ok);
+       return 1;
+
+#else
+       (void)(L);           /* unused */
+       (void)(is_periodic); /* unused */
+       return 0;
+#endif
+}
+
+
+/* mg.set_timeout for websockets */
+static int
+lwebsocket_set_timeout(lua_State *L)
+{
+       return lwebsocket_set_timer(L, 0);
+}
+
+
+/* mg.set_interval for websockets */
+static int
+lwebsocket_set_interval(lua_State *L)
+{
+       return lwebsocket_set_timer(L, 1);
+}
+
+enum {
+       LUA_ENV_TYPE_LUA_SERVER_PAGE = 0,
+       LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,
+       LUA_ENV_TYPE_LUA_WEBSOCKET = 2,
+};
+
+
+static void
+prepare_lua_request_info(struct mg_connection *conn, lua_State *L)
+{
+       const char *s;
+       int i;
+
+       /* Export mg.request_info */
+       lua_pushstring(L, "request_info");
+       lua_newtable(L);
+       reg_string(L, "request_method", conn->request_info.request_method);
+       reg_string(L, "request_uri", conn->request_info.request_uri);
+       reg_string(L, "uri", conn->request_info.local_uri);
+       reg_string(L, "http_version", conn->request_info.http_version);
+       reg_string(L, "query_string", conn->request_info.query_string);
+#if defined(MG_LEGACY_INTERFACE)
+       reg_int(L, "remote_ip", conn->request_info.remote_ip); /* remote_ip is
+                                                                 deprecated, use
+                                                                 remote_addr
+                                                                 instead */
+#endif
+       reg_string(L, "remote_addr", conn->request_info.remote_addr);
+       /* TODO (high): ip version */
+       reg_int(L, "remote_port", conn->request_info.remote_port);
+       reg_int(L, "num_headers", conn->request_info.num_headers);
+       reg_int(L, "server_port", ntohs(conn->client.lsa.sin.sin_port));
+
+       if (conn->request_info.content_length >= 0) {
+               /* reg_int64: content_length */
+               lua_pushstring(L, "content_length");
+               lua_pushnumber(
+                   L,
+                   (lua_Number)conn->request_info
+                       .content_length); /* lua_Number may be used as 52 bit integer */
+               lua_rawset(L, -3);
+       }
+       if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
+               reg_string(L, "content_type", s);
+       }
+
+       if (conn->request_info.remote_user != NULL) {
+               reg_string(L, "remote_user", conn->request_info.remote_user);
+               reg_string(L, "auth_type", "Digest");
+       }
+
+       reg_boolean(L, "https", conn->ssl != NULL);
+
+       if (conn->status_code > 0) {
+               /* Lua error handler should show the status code */
+               reg_int(L, "status", conn->status_code);
+       }
+
+       lua_pushstring(L, "http_headers");
+       lua_newtable(L);
+       for (i = 0; i < conn->request_info.num_headers; i++) {
+               reg_string(L,
+                          conn->request_info.http_headers[i].name,
+                          conn->request_info.http_headers[i].value);
+       }
+       lua_rawset(L, -3);
+
+       lua_rawset(L, -3);
+}
+
+
+void
+civetweb_open_lua_libs(lua_State *L)
+{
+       {
+               extern void luaL_openlibs(lua_State *);
+               luaL_openlibs(L);
+       }
+
+#ifdef USE_LUA_SQLITE3
+       {
+               extern int luaopen_lsqlite3(lua_State *);
+               luaopen_lsqlite3(L);
+       }
+#endif
+#ifdef USE_LUA_LUAXML
+       {
+               extern int luaopen_LuaXML_lib(lua_State *);
+               luaopen_LuaXML_lib(L);
+       }
+#endif
+#ifdef USE_LUA_FILE_SYSTEM
+       {
+               extern int luaopen_lfs(lua_State *);
+               luaopen_lfs(L);
+       }
+#endif
+#ifdef USE_LUA_BINARY
+       {
+               /* TODO (low): Test if this could be used as a replacement for bit32.
+                * Check again with Lua 5.3 later. */
+               extern int luaopen_binary(lua_State *);
+
+               luaL_requiref(L, "binary", luaopen_binary, 1);
+               lua_pop(L, 1);
+       }
+#endif
+}
+
+
+static void
+prepare_lua_environment(struct mg_context *ctx,
+                        struct mg_connection *conn,
+                        struct lua_websock_data *ws_conn_list,
+                        lua_State *L,
+                        const char *script_name,
+                        int lua_env_type)
+{
+       civetweb_open_lua_libs(L);
+
+#if LUA_VERSION_NUM == 502
+       /* Keep the "connect" method for compatibility,
+        * but do not backport it to Lua 5.1.
+        * TODO: Redesign the interface.
+        */
+       luaL_newmetatable(L, LUASOCKET);
+       lua_pushliteral(L, "__index");
+       luaL_newlib(L, luasocket_methods);
+       lua_rawset(L, -3);
+       lua_pop(L, 1);
+       lua_register(L, "connect", lsp_connect);
+#endif
+
+       /* Store context in the registry */
+       if (ctx != NULL) {
+               lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+               lua_pushlightuserdata(L, (void *)ctx);
+               lua_settable(L, LUA_REGISTRYINDEX);
+       }
+       if (ws_conn_list != NULL) {
+               lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
+               lua_pushlightuserdata(L, (void *)ws_conn_list);
+               lua_settable(L, LUA_REGISTRYINDEX);
+       }
+
+       /* Register mg module */
+       lua_newtable(L);
+
+       switch (lua_env_type) {
+       case LUA_ENV_TYPE_LUA_SERVER_PAGE:
+               reg_string(L, "lua_type", "page");
+               break;
+       case LUA_ENV_TYPE_PLAIN_LUA_PAGE:
+               reg_string(L, "lua_type", "script");
+               break;
+       case LUA_ENV_TYPE_LUA_WEBSOCKET:
+               reg_string(L, "lua_type", "websocket");
+               break;
+       }
+
+       if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE
+           || lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) {
+               reg_conn_function(L, "cry", lsp_cry, conn);
+               reg_conn_function(L, "read", lsp_read, conn);
+               reg_conn_function(L, "write", lsp_write, conn);
+               reg_conn_function(L, "keep_alive", lsp_keep_alive, conn);
+               reg_conn_function(L, "send_file", lsp_send_file, conn);
+       }
+
+       if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) {
+               reg_conn_function(L, "include", lsp_include, conn);
+               reg_conn_function(L, "redirect", lsp_redirect, conn);
+       }
+
+       if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
+               reg_function(L, "write", lwebsock_write);
+#ifdef USE_TIMERS
+               reg_function(L, "set_timeout", lwebsocket_set_timeout);
+               reg_function(L, "set_interval", lwebsocket_set_interval);
+#endif
+               /* reg_conn_function(L, "send_file", lsp_send_file, conn); */
+       }
+
+       reg_function(L, "time", lsp_get_time);
+       reg_function(L, "get_var", lsp_get_var);
+       reg_function(L, "get_mime_type", lsp_get_mime_type);
+       reg_function(L, "get_cookie", lsp_get_cookie);
+       reg_function(L, "md5", lsp_md5);
+       reg_function(L, "url_encode", lsp_url_encode);
+       reg_function(L, "url_decode", lsp_url_decode);
+       reg_function(L, "base64_encode", lsp_base64_encode);
+       reg_function(L, "base64_decode", lsp_base64_decode);
+       reg_function(L, "get_response_code_text", lsp_get_response_code_text);
+       reg_function(L, "random", lsp_random);
+       if (pf_uuid_generate.f) {
+               reg_function(L, "uuid", lsp_uuid);
+       }
+
+       reg_string(L, "version", CIVETWEB_VERSION);
+
+       reg_string(L, "script_name", script_name);
+
+       if (ctx != NULL) {
+               reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
+               reg_string(L, "auth_domain", ctx->config[AUTHENTICATION_DOMAIN]);
+#if defined(USE_WEBSOCKET)
+               reg_string(L, "websocket_root", ctx->config[WEBSOCKET_ROOT]);
+#endif
+
+               if (ctx->systemName != NULL) {
+                       reg_string(L, "system", ctx->systemName);
+               }
+       }
+
+       /* Export connection specific info */
+       if (conn != NULL) {
+               prepare_lua_request_info(conn, L);
+       }
+
+       lua_setglobal(L, "mg");
+
+       /* Register default mg.onerror function */
+       IGNORE_UNUSED_RESULT(
+           luaL_dostring(L,
+                         "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
+                         "debug.traceback(e, 1)) end"));
+
+       if (ctx != NULL) {
+               /* Preload */
+               if (ctx->config[LUA_PRELOAD_FILE] != NULL) {
+                       IGNORE_UNUSED_RESULT(luaL_dofile(L, ctx->config[LUA_PRELOAD_FILE]));
+               }
+
+               if (ctx->callbacks.init_lua != NULL) {
+                       ctx->callbacks.init_lua(conn, L);
+               }
+       }
+}
+
+
+static int
+lua_error_handler(lua_State *L)
+{
+       const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
+
+       lua_getglobal(L, "mg");
+       if (!lua_isnil(L, -1)) {
+               lua_getfield(L, -1, "write"); /* call mg.write() */
+               lua_pushstring(L, error_msg);
+               lua_pushliteral(L, "\n");
+               lua_call(L, 2, 0);
+               IGNORE_UNUSED_RESULT(
+                   luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"));
+       } else {
+               printf("Lua error: [%s]\n", error_msg);
+               IGNORE_UNUSED_RESULT(
+                   luaL_dostring(L, "print(debug.traceback(), '\\n')"));
+       }
+       /* TODO(lsm, low): leave the stack balanced */
+
+       return 0;
+}
+
+
+static void *
+lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
+{
+
+       (void)ud;
+       (void)osize; /* not used */
+
+       if (nsize == 0) {
+               mg_free(ptr);
+               return NULL;
+       }
+       return mg_realloc(ptr, nsize);
+}
+
+
+static void
+mg_exec_lua_script(struct mg_connection *conn,
+                   const char *path,
+                   const void **exports)
+{
+       int i;
+       lua_State *L;
+
+       /* Assume the script does not support keep_alive. The script may change this
+        * by calling mg.keep_alive(true). */
+       conn->must_close = 1;
+
+       /* Execute a plain Lua script. */
+       if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) {
+               prepare_lua_environment(
+                   conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
+               lua_pushcclosure(L, &lua_error_handler, 0);
+
+               if (exports != NULL) {
+#if LUA_VERSION_NUM > 501
+                       lua_pushglobaltable(L);
+                       for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
+                               lua_CFunction func;
+                               lua_pushstring(L, (const char *)(exports[i]));
+                               *(const void **)(&func) = exports[i + 1];
+                               lua_pushcclosure(L, func, 0);
+                               lua_rawset(L, -3);
+                       }
+#else
+                       for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
+                               lua_CFunction func;
+                               const char *name = (const char *)(exports[i]);
+                               *(const void **)(&func) = exports[i + 1];
+                               lua_register(L, name, func);
+                       }
+#endif
+               }
+
+               if (luaL_loadfile(L, path) != 0) {
+                       lua_error_handler(L);
+               }
+               lua_pcall(L, 0, 0, -2);
+               lua_close(L);
+       }
+}
+
+
+static int
+handle_lsp_request(struct mg_connection *conn,
+                   const char *path,
+                   struct file *filep,
+                   struct lua_State *ls)
+{
+       void *p = NULL;
+       lua_State *L = NULL;
+       int error = 1;
+       struct file filesize = STRUCT_FILE_INITIALIZER;
+
+       /* Assume the script does not support keep_alive. The script may change this
+        * by calling mg.keep_alive(true). */
+       conn->must_close = 1;
+
+       /* We need both mg_stat to get file size, and mg_fopen to get fd */
+       if (!mg_stat(conn, path, &filesize)) {
+
+               /* File not found */
+               if (ls == NULL) {
+                       send_http_error(conn, 500, "Error: File %s not found", path);
+               } else {
+                       luaL_error(ls, "File [%s] not found", path);
+               }
+
+               goto cleanup_handle_lsp_request;
+       }
+
+       if (!mg_fopen(conn, path, "r", filep)) {
+
+               /* File not found or not accessible */
+               if (ls == NULL) {
+                       send_http_error(conn,
+                                       500,
+                                       "Error: Cannot open script file %s",
+                                       path);
+               } else {
+                       luaL_error(ls, "Cannot  [%s] not found", path);
+               }
+
+               goto cleanup_handle_lsp_request;
+       }
+
+       /* TODO: Operations mg_fopen and mg_stat should do what their names
+        * indicate. They should not fill in different members of the same
+        * struct file.
+        * See Github issue #225 */
+       filep->size = filesize.size;
+
+       if (filep->membuf == NULL
+           && (p = mmap(NULL,
+                        (size_t)filep->size,
+                        PROT_READ,
+                        MAP_PRIVATE,
+                        fileno(filep->fp),
+                        0)) == MAP_FAILED) {
+
+               /* mmap failed */
+               if (ls == NULL) {
+                       send_http_error(
+                           conn,
+                           500,
+                           "Error: Cannot open script\nFile %s can not be mapped",
+                           path);
+               } else {
+                       luaL_error(ls,
+                                  "mmap(%s, %zu, %d): %s",
+                                  path,
+                                  (size_t)filep->size,
+                                  fileno(filep->fp),
+                                  strerror(errno));
+               }
+
+               goto cleanup_handle_lsp_request;
+       }
+
+       if (ls != NULL) {
+               L = ls;
+       } else {
+               L = lua_newstate(lua_allocator, NULL);
+               if (L == NULL) {
+                       send_http_error(
+                           conn,
+                           500,
+                           "%s",
+                           "Error: Cannot execute script\nlua_newstate failed");
+
+                       goto cleanup_handle_lsp_request;
+               }
+               prepare_lua_environment(
+                   conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
+       }
+
+       /* Lua state is ready to use */
+       /* We're not sending HTTP headers here, Lua page must do it. */
+       error = lsp(conn,
+                   path,
+                   (filep->membuf == NULL) ? (const char *)p
+                                           : (const char *)filep->membuf,
+                   filep->size,
+                   L);
+
+
+cleanup_handle_lsp_request:
+
+       if (L != NULL && ls == NULL)
+               lua_close(L);
+       if (p != NULL)
+               munmap(p, filep->size);
+       mg_fclose(filep);
+
+       return error;
+}
+
+
+#ifdef USE_WEBSOCKET
+struct mg_shared_lua_websocket_list {
+       struct lua_websock_data ws;
+       struct mg_shared_lua_websocket_list *next;
+};
+
+
+static void *
+lua_websocket_new(const char *script, struct mg_connection *conn)
+{
+       struct mg_shared_lua_websocket_list **shared_websock_list =
+           &(conn->ctx->shared_lua_websockets);
+       struct lua_websock_data *ws;
+       int err, ok = 0;
+
+       assert(conn->lua_websocket_state == NULL);
+
+       /* lock list (mg_context global) */
+       mg_lock_context(conn->ctx);
+       while (*shared_websock_list) {
+               /* check if ws already in list */
+               if (0 == strcmp(script, (*shared_websock_list)->ws.script)) {
+                       break;
+               }
+               shared_websock_list = &((*shared_websock_list)->next);
+       }
+
+       if (*shared_websock_list == NULL) {
+               /* add ws to list */
+               *shared_websock_list = (struct mg_shared_lua_websocket_list *)
+                   mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
+               if (*shared_websock_list == NULL) {
+                       mg_unlock_context(conn->ctx);
+                       mg_cry(conn, "Cannot create shared websocket struct, OOM");
+                       return NULL;
+               }
+               /* init ws list element */
+               ws = &(*shared_websock_list)->ws;
+               ws->script = mg_strdup(script); /* TODO (low): handle OOM */
+               pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr);
+               (void)pthread_mutex_lock(&(ws->ws_mutex));
+               ws->state = lua_newstate(lua_allocator, NULL);
+               ws->conn[0] = conn;
+               ws->references = 1;
+               prepare_lua_environment(
+                   conn->ctx, NULL, ws, ws->state, script, LUA_ENV_TYPE_LUA_WEBSOCKET);
+               err = luaL_loadfile(ws->state, script);
+               if (err != 0) {
+                       lua_cry(conn, err, ws->state, script, "load");
+               }
+               err = lua_pcall(ws->state, 0, 0, 0);
+               if (err != 0) {
+                       lua_cry(conn, err, ws->state, script, "init");
+               }
+       } else {
+               /* inc ref count */
+               ws = &(*shared_websock_list)->ws;
+               (void)pthread_mutex_lock(&(ws->ws_mutex));
+               (*shared_websock_list)->ws.conn[(ws->references)++] = conn;
+       }
+       mg_unlock_context(conn->ctx);
+
+       /* call add */
+       lua_getglobal(ws->state, "open");
+       lua_newtable(ws->state);
+       prepare_lua_request_info(conn, ws->state);
+       lua_pushstring(ws->state, "client");
+       lua_pushlightuserdata(ws->state, (void *)conn);
+       lua_rawset(ws->state, -3);
+
+       err = lua_pcall(ws->state, 1, 1, 0);
+       if (err != 0) {
+               lua_cry(conn, err, ws->state, script, "open handler");
+       } else {
+               if (lua_isboolean(ws->state, -1)) {
+                       ok = lua_toboolean(ws->state, -1);
+               }
+               lua_pop(ws->state, 1);
+       }
+       if (!ok) {
+               /* Remove from ws connection list. */
+               /* TODO (mid): Check if list entry and Lua state needs to be deleted
+                * (see websocket_close). */
+               (*shared_websock_list)->ws.conn[--(ws->references)] = 0;
+       }
+
+       (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+       return ok ? (void *)ws : NULL;
+}
+
+
+static int
+lua_websocket_data(struct mg_connection *conn,
+                   int bits,
+                   char *data,
+                   size_t data_len,
+                   void *ws_arg)
+{
+       struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+       int err, ok = 0;
+
+       assert(ws != NULL);
+       assert(ws->state != NULL);
+
+       (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+       lua_getglobal(ws->state, "data");
+       lua_newtable(ws->state);
+       lua_pushstring(ws->state, "client");
+       lua_pushlightuserdata(ws->state, (void *)conn);
+       lua_rawset(ws->state, -3);
+       lua_pushstring(ws->state, "bits"); /* TODO: dont use "bits" but fields with
+                                             a meaning according to
+                                             http://tools.ietf.org/html/rfc6455,
+                                             section 5.2 */
+       lua_pushnumber(ws->state, bits);
+       lua_rawset(ws->state, -3);
+       lua_pushstring(ws->state, "data");
+       lua_pushlstring(ws->state, data, data_len);
+       lua_rawset(ws->state, -3);
+
+       err = lua_pcall(ws->state, 1, 1, 0);
+       if (err != 0) {
+               lua_cry(conn, err, ws->state, ws->script, "data handler");
+       } else {
+               if (lua_isboolean(ws->state, -1)) {
+                       ok = lua_toboolean(ws->state, -1);
+               }
+               lua_pop(ws->state, 1);
+       }
+       (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+       return ok;
+}
+
+
+static int
+lua_websocket_ready(struct mg_connection *conn, void *ws_arg)
+{
+       struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+       int err, ok = 0;
+
+       assert(ws != NULL);
+       assert(ws->state != NULL);
+
+       (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+       lua_getglobal(ws->state, "ready");
+       lua_newtable(ws->state);
+       lua_pushstring(ws->state, "client");
+       lua_pushlightuserdata(ws->state, (void *)conn);
+       lua_rawset(ws->state, -3);
+       err = lua_pcall(ws->state, 1, 1, 0);
+       if (err != 0) {
+               lua_cry(conn, err, ws->state, ws->script, "ready handler");
+       } else {
+               if (lua_isboolean(ws->state, -1)) {
+                       ok = lua_toboolean(ws->state, -1);
+               }
+               lua_pop(ws->state, 1);
+       }
+
+       (void)pthread_mutex_unlock(&(ws->ws_mutex));
+
+       return ok;
+}
+
+
+static void
+lua_websocket_close(struct mg_connection *conn, void *ws_arg)
+{
+       struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
+       struct mg_shared_lua_websocket_list **shared_websock_list =
+           &(conn->ctx->shared_lua_websockets);
+       int err = 0;
+       unsigned i;
+
+       assert(ws != NULL);
+       assert(ws->state != NULL);
+
+       (void)pthread_mutex_lock(&(ws->ws_mutex));
+
+       lua_getglobal(ws->state, "close");
+       lua_newtable(ws->state);
+       lua_pushstring(ws->state, "client");
+       lua_pushlightuserdata(ws->state, (void *)conn);
+       lua_rawset(ws->state, -3);
+
+       err = lua_pcall(ws->state, 1, 0, 0);
+       if (err != 0) {
+               lua_cry(conn, err, ws->state, ws->script, "close handler");
+       }
+       for (i = 0; i < ws->references; i++) {
+               if (ws->conn[i] == conn) {
+                       ws->references--;
+                       ws->conn[i] = ws->conn[ws->references];
+               }
+       }
+       /* TODO: Delete lua_websock_data and remove it from the websocket list.
+          This must only be done, when all connections are closed, and all
+          asynchronous operations and timers are completed/expired. */
+       (void)shared_websock_list; /* shared_websock_list unused (see open TODO) */
+
+       (void)pthread_mutex_unlock(&(ws->ws_mutex));
+}
+#endif
+
+
+static void
+lua_init_optional_libraries(void)
+{
+#if !defined(_WIN32)
+       void *dll_handle = dlopen("libuuid.so", RTLD_LAZY);
+       pf_uuid_generate.p = dlsym(dll_handle, "uuid_generate");
+#else
+       pf_uuid_generate.p = 0;
+#endif
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/timer.inl b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/timer.inl
new file mode 100644 (file)
index 0000000..a9bcf26
--- /dev/null
@@ -0,0 +1,150 @@
+
+#if !defined(MAX_TIMERS)
+#define MAX_TIMERS MAX_WORKER_THREADS
+#endif
+
+typedef int (*taction)(void *arg);
+
+struct ttimer {
+       double time;
+       double period;
+       taction action;
+       void *arg;
+};
+
+struct ttimers {
+       pthread_t threadid;               /* Timer thread ID */
+       pthread_mutex_t mutex;            /* Protects timer lists */
+       struct ttimer timers[MAX_TIMERS]; /* List of timers */
+       unsigned timer_count;             /* Current size of timer list */
+};
+
+static int
+timer_add(struct mg_context *ctx,
+          double next_time,
+          double period,
+          int is_relative,
+          taction action,
+          void *arg)
+{
+       unsigned u, v;
+       int error = 0;
+       struct timespec now;
+
+       if (ctx->stop_flag) {
+               return 0;
+       }
+
+       if (is_relative) {
+               clock_gettime(CLOCK_MONOTONIC, &now);
+               next_time += now.tv_sec;
+               next_time += now.tv_nsec * 1.0E-9;
+       }
+
+       pthread_mutex_lock(&ctx->timers->mutex);
+       if (ctx->timers->timer_count == MAX_TIMERS) {
+               error = 1;
+       } else {
+               for (u = 0; u < ctx->timers->timer_count; u++) {
+                       if (ctx->timers->timers[u].time < next_time) {
+                               for (v = ctx->timers->timer_count; v > u; v--) {
+                                       ctx->timers->timers[v] = ctx->timers->timers[v - 1];
+                               }
+                               break;
+                       }
+               }
+               ctx->timers->timers[u].time = next_time;
+               ctx->timers->timers[u].period = period;
+               ctx->timers->timers[u].action = action;
+               ctx->timers->timers[u].arg = arg;
+               ctx->timers->timer_count++;
+       }
+       pthread_mutex_unlock(&ctx->timers->mutex);
+       return error;
+}
+
+static void
+timer_thread_run(void *thread_func_param)
+{
+       struct mg_context *ctx = (struct mg_context *)thread_func_param;
+       struct timespec now;
+       double d;
+       unsigned u;
+       int re_schedule;
+       struct ttimer t;
+
+       mg_set_thread_name("timer");
+
+       if (ctx->callbacks.init_thread) {
+               /* Timer thread */
+               ctx->callbacks.init_thread(ctx, 2);
+       }
+
+#if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
+       /* TODO */
+       while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &request, &request)
+              == EINTR) { /*nop*/
+               ;
+       }
+#else
+       clock_gettime(CLOCK_MONOTONIC, &now);
+       d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
+       while (ctx->stop_flag == 0) {
+               pthread_mutex_lock(&ctx->timers->mutex);
+               if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
+                       t = ctx->timers->timers[0];
+                       for (u = 1; u < ctx->timers->timer_count; u++) {
+                               ctx->timers->timers[u - 1] = ctx->timers->timers[u];
+                       }
+                       ctx->timers->timer_count--;
+                       pthread_mutex_unlock(&ctx->timers->mutex);
+                       re_schedule = t.action(t.arg);
+                       if (re_schedule && (t.period > 0)) {
+                               timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
+                       }
+                       continue;
+               } else {
+                       pthread_mutex_unlock(&ctx->timers->mutex);
+               }
+               mg_sleep(1);
+               clock_gettime(CLOCK_MONOTONIC, &now);
+               d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
+       }
+#endif
+}
+
+#ifdef _WIN32
+static unsigned __stdcall timer_thread(void *thread_func_param)
+{
+       timer_thread_run(thread_func_param);
+       return 0;
+}
+#else
+static void *
+timer_thread(void *thread_func_param)
+{
+       timer_thread_run(thread_func_param);
+       return NULL;
+}
+#endif /* _WIN32 */
+
+static int
+timers_init(struct mg_context *ctx)
+{
+       ctx->timers = (struct ttimers *)mg_calloc(sizeof(struct ttimers), 1);
+       (void)pthread_mutex_init(&ctx->timers->mutex, NULL);
+
+       /* Start timer thread */
+       mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
+
+       return 0;
+}
+
+static void
+timers_exit(struct mg_context *ctx)
+{
+       if (ctx->timers) {
+               (void)pthread_mutex_destroy(&ctx->timers->mutex);
+               mg_free(ctx->timers);
+       }
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/libhttp-1.8.tar.gz b/LinuxGUI/Ventoy2Disk/Lib/libhttp/libhttp-1.8.tar.gz
new file mode 100644 (file)
index 0000000..95a3781
Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/libhttp/libhttp-1.8.tar.gz differ
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/COPYING b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/COPYING
new file mode 100644 (file)
index 0000000..fc4fbf7
--- /dev/null
@@ -0,0 +1,10 @@
+
+Licensing of XZ Embedded
+========================
+
+    All the files in this package have been written by Lasse Collin
+    and/or Igor Pavlov. All these files have been put into the
+    public domain. You can do whatever you want with these files.
+
+    As usual, this software is provided "as is", without any warranty.
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/README b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/README
new file mode 100644 (file)
index 0000000..566d604
--- /dev/null
@@ -0,0 +1,163 @@
+
+XZ Embedded
+===========
+
+    XZ Embedded is a relatively small, limited implementation of the .xz
+    file format. Currently only decoding is implemented.
+
+    XZ Embedded was written for use in the Linux kernel, but the code can
+    be easily used in other environments too, including regular userspace
+    applications. See userspace/xzminidec.c for an example program.
+
+    This README contains information that is useful only when the copy
+    of XZ Embedded isn't part of the Linux kernel tree. You should also
+    read linux/Documentation/xz.txt even if you aren't using XZ Embedded
+    as part of Linux; information in that file is not repeated in this
+    README.
+
+Compiling the Linux kernel module
+
+    The xz_dec module depends on crc32 module, so make sure that you have
+    it enabled (CONFIG_CRC32).
+
+    Building the xz_dec and xz_dec_test modules without support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
+
+    Building the xz_dec and xz_dec_test modules with support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
+                CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
+                CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
+                CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
+
+    If you want only one or a few of the BCJ filters, omit the appropriate
+    variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
+    code shared between all BCJ filters.
+
+    Most people don't need the xz_dec_test module. You can skip building
+    it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
+
+Compiler requirements
+
+    XZ Embedded should compile as either GNU-C89 (used in the Linux
+    kernel) or with any C99 compiler. Getting the code to compile with
+    non-GNU C89 compiler or a C++ compiler should be quite easy as
+    long as there is a data type for unsigned 64-bit integer (or the
+    code is modified not to support large files, which needs some more
+    care than just using 32-bit integer instead of 64-bit).
+
+    If you use GCC, try to use a recent version. For example, on x86-32,
+    xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
+    compiled with GCC 4.3.3.
+
+Embedding into userspace applications
+
+    To embed the XZ decoder, copy the following files into a single
+    directory in your source code tree:
+
+        linux/include/linux/xz.h
+        linux/lib/xz/xz_crc32.c
+        linux/lib/xz/xz_dec_lzma2.c
+        linux/lib/xz/xz_dec_stream.c
+        linux/lib/xz/xz_lzma2.h
+        linux/lib/xz/xz_private.h
+        linux/lib/xz/xz_stream.h
+        userspace/xz_config.h
+
+    Alternatively, xz.h may be placed into a different directory but then
+    that directory must be in the compiler include path when compiling
+    the .c files.
+
+    Your code should use only the functions declared in xz.h. The rest of
+    the .h files are meant only for internal use in XZ Embedded.
+
+    You may want to modify xz_config.h to be more suitable for your build
+    environment. Probably you should at least skim through it even if the
+    default file works as is.
+
+Integrity check support
+
+    XZ Embedded always supports the integrity check types None and
+    CRC32. Support for CRC64 is optional. SHA-256 is currently not
+    supported in XZ Embedded although the .xz format does support it.
+    The xz tool from XZ Utils uses CRC64 by default, but CRC32 is usually
+    enough in embedded systems to keep the code size smaller.
+
+    If you want support for CRC64, you need to copy linux/lib/xz/xz_crc64.c
+    into your application, and #define XZ_USE_CRC64 in xz_config.h or in
+    compiler flags.
+
+    When using the internal CRC32 or CRC64, their lookup tables need to be
+    initialized with xz_crc32_init() and xz_crc64_init(), respectively.
+    See xz.h for details.
+
+    To use external CRC32 or CRC64 code instead of the code from
+    xz_crc32.c or xz_crc64.c, the following #defines may be used
+    in xz_config.h or in compiler flags:
+
+        #define XZ_INTERNAL_CRC32 0
+        #define XZ_INTERNAL_CRC64 0
+
+    Then it is up to you to provide compatible xz_crc32() or xz_crc64()
+    functions.
+
+    If the .xz file being decompressed uses an integrity check type that
+    isn't supported by XZ Embedded, it is treated as an error and the
+    file cannot be decompressed. For multi-call mode, this can be modified
+    by #defining XZ_DEC_ANY_CHECK. Then xz_dec_run() will return
+    XZ_UNSUPPORTED_CHECK when unsupported check type is detected. After
+    that decompression can be continued normally except that the
+    integrity check won't be verified. In single-call mode there's
+    no way to continue decoding, so XZ_DEC_ANY_CHECK is almost useless
+    in single-call mode.
+
+BCJ filter support
+
+    If you want support for one or more BCJ filters, you need to copy also
+    linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
+    #defines in xz_config.h or in compiler flags. You don't need these
+    #defines in the code that just uses XZ Embedded via xz.h, but having
+    them always #defined doesn't hurt either.
+
+        #define             Instruction set     BCJ filter endianness
+        XZ_DEC_X86          x86-32 or x86-64    Little endian only
+        XZ_DEC_POWERPC      PowerPC             Big endian only
+        XZ_DEC_IA64         Itanium (IA-64)     Big or little endian
+        XZ_DEC_ARM          ARM                 Little endian only
+        XZ_DEC_ARMTHUMB     ARM-Thumb           Little endian only
+        XZ_DEC_SPARC        SPARC               Big or little endian
+
+    While some architectures are (partially) bi-endian, the endianness
+    setting doesn't change the endianness of the instructions on all
+    architectures. That's why Itanium and SPARC filters work for both big
+    and little endian executables (Itanium has little endian instructions
+    and SPARC has big endian instructions).
+
+    There currently is no filter for little endian PowerPC or big endian
+    ARM or ARM-Thumb. Implementing filters for them can be considered if
+    there is a need for such filters in real-world applications.
+
+Notes about shared libraries
+
+    If you are including XZ Embedded into a shared library, you very
+    probably should rename the xz_* functions to prevent symbol
+    conflicts in case your library is linked against some other library
+    or application that also has XZ Embedded in it (which may even be
+    a different version of XZ Embedded). TODO: Provide an easy way
+    to do this.
+
+    Please don't create a shared library of XZ Embedded itself unless
+    it is fine to rebuild everything depending on that shared library
+    everytime you upgrade to a newer version of XZ Embedded. There are
+    no API or ABI stability guarantees between different versions of
+    XZ Embedded.
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/Documentation/xz.txt b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/Documentation/xz.txt
new file mode 100644 (file)
index 0000000..68329ac
--- /dev/null
@@ -0,0 +1,122 @@
+
+XZ data compression in Linux
+============================
+
+Introduction
+
+    XZ is a general purpose data compression format with high compression
+    ratio and relatively fast decompression. The primary compression
+    algorithm (filter) is LZMA2. Additional filters can be used to improve
+    compression ratio even further. E.g. Branch/Call/Jump (BCJ) filters
+    improve compression ratio of executable data.
+
+    The XZ decompressor in Linux is called XZ Embedded. It supports
+    the LZMA2 filter and optionally also BCJ filters. CRC32 is supported
+    for integrity checking. The home page of XZ Embedded is at
+    <http://tukaani.org/xz/embedded.html>, where you can find the
+    latest version and also information about using the code outside
+    the Linux kernel.
+
+    For userspace, XZ Utils provide a zlib-like compression library
+    and a gzip-like command line tool. XZ Utils can be downloaded from
+    <http://tukaani.org/xz/>.
+
+XZ related components in the kernel
+
+    The xz_dec module provides XZ decompressor with single-call (buffer
+    to buffer) and multi-call (stateful) APIs. The usage of the xz_dec
+    module is documented in include/linux/xz.h.
+
+    The xz_dec_test module is for testing xz_dec. xz_dec_test is not
+    useful unless you are hacking the XZ decompressor. xz_dec_test
+    allocates a char device major dynamically to which one can write
+    .xz files from userspace. The decompressed output is thrown away.
+    Keep an eye on dmesg to see diagnostics printed by xz_dec_test.
+    See the xz_dec_test source code for the details.
+
+    For decompressing the kernel image, initramfs, and initrd, there
+    is a wrapper function in lib/decompress_unxz.c. Its API is the
+    same as in other decompress_*.c files, which is defined in
+    include/linux/decompress/generic.h.
+
+    scripts/xz_wrap.sh is a wrapper for the xz command line tool found
+    from XZ Utils. The wrapper sets compression options to values suitable
+    for compressing the kernel image.
+
+    For kernel makefiles, two commands are provided for use with
+    $(call if_needed). The kernel image should be compressed with
+    $(call if_needed,xzkern) which will use a BCJ filter and a big LZMA2
+    dictionary. It will also append a four-byte trailer containing the
+    uncompressed size of the file, which is needed by the boot code.
+    Other things should be compressed with $(call if_needed,xzmisc)
+    which will use no BCJ filter and 1 MiB LZMA2 dictionary.
+
+Notes on compression options
+
+    Since the XZ Embedded supports only streams with no integrity check or
+    CRC32, make sure that you don't use some other integrity check type
+    when encoding files that are supposed to be decoded by the kernel. With
+    liblzma, you need to use either LZMA_CHECK_NONE or LZMA_CHECK_CRC32
+    when encoding. With the xz command line tool, use --check=none or
+    --check=crc32.
+
+    Using CRC32 is strongly recommended unless there is some other layer
+    which will verify the integrity of the uncompressed data anyway.
+    Double checking the integrity would probably be waste of CPU cycles.
+    Note that the headers will always have a CRC32 which will be validated
+    by the decoder; you can only change the integrity check type (or
+    disable it) for the actual uncompressed data.
+
+    In userspace, LZMA2 is typically used with dictionary sizes of several
+    megabytes. The decoder needs to have the dictionary in RAM, thus big
+    dictionaries cannot be used for files that are intended to be decoded
+    by the kernel. 1 MiB is probably the maximum reasonable dictionary
+    size for in-kernel use (maybe more is OK for initramfs). The presets
+    in XZ Utils may not be optimal when creating files for the kernel,
+    so don't hesitate to use custom settings. Example:
+
+        xz --check=crc32 --lzma2=dict=512KiB inputfile
+
+    An exception to above dictionary size limitation is when the decoder
+    is used in single-call mode. Decompressing the kernel itself is an
+    example of this situation. In single-call mode, the memory usage
+    doesn't depend on the dictionary size, and it is perfectly fine to
+    use a big dictionary: for maximum compression, the dictionary should
+    be at least as big as the uncompressed data itself.
+
+Future plans
+
+    Creating a limited XZ encoder may be considered if people think it is
+    useful. LZMA2 is slower to compress than e.g. Deflate or LZO even at
+    the fastest settings, so it isn't clear if LZMA2 encoder is wanted
+    into the kernel.
+
+    Support for limited random-access reading is planned for the
+    decompression code. I don't know if it could have any use in the
+    kernel, but I know that it would be useful in some embedded projects
+    outside the Linux kernel.
+
+Conformance to the .xz file format specification
+
+    There are a couple of corner cases where things have been simplified
+    at expense of detecting errors as early as possible. These should not
+    matter in practice all, since they don't cause security issues. But
+    it is good to know this if testing the code e.g. with the test files
+    from XZ Utils.
+
+Reporting bugs
+
+    Before reporting a bug, please check that it's not fixed already
+    at upstream. See <http://tukaani.org/xz/embedded.html> to get the
+    latest code.
+
+    Report bugs to <lasse.collin@tukaani.org> or visit #tukaani on
+    Freenode and talk to Larhzu. I don't actively read LKML or other
+    kernel-related mailing lists, so if there's something I should know,
+    you should email to me personally or use IRC.
+
+    Don't bother Igor Pavlov with questions about the XZ implementation
+    in the kernel or about XZ Utils. While these two implementations
+    include essential code that is directly based on Igor Pavlov's code,
+    these implementations aren't maintained nor supported by him.
+
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/decompress/unxz.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/decompress/unxz.h
new file mode 100644 (file)
index 0000000..41728fc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef DECOMPRESS_UNXZ_H
+#define DECOMPRESS_UNXZ_H
+
+int unxz(unsigned char *in, int in_size,
+        int (*fill)(void *dest, unsigned int size),
+        int (*flush)(void *src, unsigned int size),
+        unsigned char *out, int *in_used,
+        void (*error)(char *x));
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/xz.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/include/linux/xz.h
new file mode 100644 (file)
index 0000000..0a4b38d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+#      include <linux/stddef.h>
+#      include <linux/types.h>
+#else
+#      include <stddef.h>
+#      include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+#      define XZ_EXTERN extern
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE:              Single-call mode. This uses less RAM than
+ *                          than multi-call modes, because the LZMA2
+ *                          dictionary doesn't need to be allocated as
+ *                          part of the decoder state. All required data
+ *                          structures are allocated at initialization,
+ *                          so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC:            Multi-call mode with preallocated LZMA2
+ *                          dictionary buffer. All data structures are
+ *                          allocated at initialization, so xz_dec_run()
+ *                          cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC:            Multi-call mode. The LZMA2 dictionary is
+ *                          allocated once the required size has been
+ *                          parsed from the stream headers. If the
+ *                          allocation fails, xz_dec_run() will return
+ *                          XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode {
+       XZ_SINGLE,
+       XZ_PREALLOC,
+       XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK:                  Everything is OK so far. More input or more
+ *                          output space is required to continue. This
+ *                          return code is possible only in multi-call mode
+ *                          (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END:          Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK:   Integrity check type is not supported. Decoding
+ *                          is still possible in multi-call mode by simply
+ *                          calling xz_dec_run() again.
+ *                          Note that this return value is used only if
+ *                          XZ_DEC_ANY_CHECK was defined at build time,
+ *                          which is not used in the kernel. Unsupported
+ *                          check types return XZ_OPTIONS_ERROR if
+ *                          XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR:           Allocating memory failed. This return code is
+ *                          possible only if the decoder was initialized
+ *                          with XZ_DYNALLOC. The amount of memory that was
+ *                          tried to be allocated was no more than the
+ *                          dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR:      A bigger LZMA2 dictionary would be needed than
+ *                          allowed by the dict_max argument given to
+ *                          xz_dec_init(). This return value is possible
+ *                          only in multi-call mode (XZ_PREALLOC or
+ *                          XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ *                          ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR:        File format was not recognized (wrong magic
+ *                          bytes).
+ * @XZ_OPTIONS_ERROR:       This implementation doesn't support the requested
+ *                          compression options. In the decoder this means
+ *                          that the header CRC32 matches, but the header
+ *                          itself specifies something that we don't support.
+ * @XZ_DATA_ERROR:          Compressed data is corrupt.
+ * @XZ_BUF_ERROR:           Cannot make any progress. Details are slightly
+ *                          different between multi-call and single-call
+ *                          mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret {
+       XZ_OK,
+       XZ_STREAM_END,
+       XZ_UNSUPPORTED_CHECK,
+       XZ_MEM_ERROR,
+       XZ_MEMLIMIT_ERROR,
+       XZ_FORMAT_ERROR,
+       XZ_OPTIONS_ERROR,
+       XZ_DATA_ERROR,
+       XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in:         Beginning of the input buffer. This may be NULL if and only
+ *              if in_pos is equal to in_size.
+ * @in_pos:     Current position in the input buffer. This must not exceed
+ *              in_size.
+ * @in_size:    Size of the input buffer
+ * @out:        Beginning of the output buffer. This may be NULL if and only
+ *              if out_pos is equal to out_size.
+ * @out_pos:    Current position in the output buffer. This must not exceed
+ *              out_size.
+ * @out_size:   Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+       const uint8_t *in;
+       size_t in_pos;
+       size_t in_size;
+
+       uint8_t *out;
+       size_t out_pos;
+       size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode:       Operation mode
+ * @dict_max:   Maximum size of the LZMA2 dictionary (history buffer) for
+ *              multi-call decoding. This is ignored in single-call mode
+ *              (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ *              or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ *              in practice), so other values for dict_max don't make sense.
+ *              In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ *              512 KiB, and 1 MiB are probably the only reasonable values,
+ *              except for kernel and initramfs images where a bigger
+ *              dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s:          Decoder state allocated using xz_dec_init()
+ * @b:          Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s:          Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s:          Decoder state allocated using xz_dec_init(). If s is NULL,
+ *              this function does nothing.
+ */
+XZ_EXTERN void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+#      ifdef __KERNEL__
+#              define XZ_INTERNAL_CRC32 0
+#      else
+#              define XZ_INTERNAL_CRC32 1
+#      endif
+#endif
+
+/*
+ * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
+ * implementation is needed too.
+ */
+#ifndef XZ_USE_CRC64
+#      undef XZ_INTERNAL_CRC64
+#      define XZ_INTERNAL_CRC64 0
+#endif
+#ifndef XZ_INTERNAL_CRC64
+#      ifdef __KERNEL__
+#              error Using CRC64 in the kernel has not been implemented.
+#      else
+#              define XZ_INTERNAL_CRC64 1
+#      endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+
+#if XZ_INTERNAL_CRC64
+/*
+ * This must be called before any other xz_* function (except xz_crc32_init())
+ * to initialize the CRC64 lookup table.
+ */
+XZ_EXTERN void xz_crc64_init(void);
+
+/*
+ * Update CRC64 value using the polynomial from ECMA-182. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/decompress_unxz.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/decompress_unxz.c
new file mode 100644 (file)
index 0000000..01aff84
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * Important notes about in-place decompression
+ *
+ * At least on x86, the kernel is decompressed in place: the compressed data
+ * is placed to the end of the output buffer, and the decompressor overwrites
+ * most of the compressed data. There must be enough safety margin to
+ * guarantee that the write position is always behind the read position.
+ *
+ * The safety margin for XZ with LZMA2 or BCJ+LZMA2 is calculated below.
+ * Note that the margin with XZ is bigger than with Deflate (gzip)!
+ *
+ * The worst case for in-place decompression is that the beginning of
+ * the file is compressed extremely well, and the rest of the file is
+ * uncompressible. Thus, we must look for worst-case expansion when the
+ * compressor is encoding uncompressible data.
+ *
+ * The structure of the .xz file in case of a compresed kernel is as follows.
+ * Sizes (as bytes) of the fields are in parenthesis.
+ *
+ *    Stream Header (12)
+ *    Block Header:
+ *      Block Header (8-12)
+ *      Compressed Data (N)
+ *      Block Padding (0-3)
+ *      CRC32 (4)
+ *    Index (8-20)
+ *    Stream Footer (12)
+ *
+ * Normally there is exactly one Block, but let's assume that there are
+ * 2-4 Blocks just in case. Because Stream Header and also Block Header
+ * of the first Block don't make the decompressor produce any uncompressed
+ * data, we can ignore them from our calculations. Block Headers of possible
+ * additional Blocks have to be taken into account still. With these
+ * assumptions, it is safe to assume that the total header overhead is
+ * less than 128 bytes.
+ *
+ * Compressed Data contains LZMA2 or BCJ+LZMA2 encoded data. Since BCJ
+ * doesn't change the size of the data, it is enough to calculate the
+ * safety margin for LZMA2.
+ *
+ * LZMA2 stores the data in chunks. Each chunk has a header whose size is
+ * a maximum of 6 bytes, but to get round 2^n numbers, let's assume that
+ * the maximum chunk header size is 8 bytes. After the chunk header, there
+ * may be up to 64 KiB of actual payload in the chunk. Often the payload is
+ * quite a bit smaller though; to be safe, let's assume that an average
+ * chunk has only 32 KiB of payload.
+ *
+ * The maximum uncompressed size of the payload is 2 MiB. The minimum
+ * uncompressed size of the payload is in practice never less than the
+ * payload size itself. The LZMA2 format would allow uncompressed size
+ * to be less than the payload size, but no sane compressor creates such
+ * files. LZMA2 supports storing uncompressible data in uncompressed form,
+ * so there's never a need to create payloads whose uncompressed size is
+ * smaller than the compressed size.
+ *
+ * The assumption, that the uncompressed size of the payload is never
+ * smaller than the payload itself, is valid only when talking about
+ * the payload as a whole. It is possible that the payload has parts where
+ * the decompressor consumes more input than it produces output. Calculating
+ * the worst case for this would be tricky. Instead of trying to do that,
+ * let's simply make sure that the decompressor never overwrites any bytes
+ * of the payload which it is currently reading.
+ *
+ * Now we have enough information to calculate the safety margin. We need
+ *   - 128 bytes for the .xz file format headers;
+ *   - 8 bytes per every 32 KiB of uncompressed size (one LZMA2 chunk header
+ *     per chunk, each chunk having average payload size of 32 KiB); and
+ *   - 64 KiB (biggest possible LZMA2 chunk payload size) to make sure that
+ *     the decompressor never overwrites anything from the LZMA2 chunk
+ *     payload it is currently reading.
+ *
+ * We get the following formula:
+ *
+ *    safety_margin = 128 + uncompressed_size * 8 / 32768 + 65536
+ *                  = 128 + (uncompressed_size >> 12) + 65536
+ *
+ * For comparison, according to arch/x86/boot/compressed/misc.c, the
+ * equivalent formula for Deflate is this:
+ *
+ *    safety_margin = 18 + (uncompressed_size >> 12) + 32768
+ *
+ * Thus, when updating Deflate-only in-place kernel decompressor to
+ * support XZ, the fixed overhead has to be increased from 18+32768 bytes
+ * to 128+65536 bytes.
+ */
+
+/*
+ * STATIC is defined to "static" if we are being built for kernel
+ * decompression (pre-boot code). <linux/decompress/mm.h> will define
+ * STATIC to empty if it wasn't already defined. Since we will need to
+ * know later if we are being used for kernel decompression, we define
+ * XZ_PREBOOT here.
+ */
+#ifdef STATIC
+#      define XZ_PREBOOT
+#endif
+#ifdef __KERNEL__
+#      include <linux/decompress/mm.h>
+#endif
+#define XZ_EXTERN STATIC
+
+#ifndef XZ_PREBOOT
+#      include <linux/slab.h>
+#      include <linux/xz.h>
+#else
+/*
+ * Use the internal CRC32 code instead of kernel's CRC32 module, which
+ * is not available in early phase of booting.
+ */
+#define XZ_INTERNAL_CRC32 1
+
+/*
+ * For boot time use, we enable only the BCJ filter of the current
+ * architecture or none if no BCJ filter is available for the architecture.
+ */
+#ifdef CONFIG_X86
+#      define XZ_DEC_X86
+#endif
+#ifdef CONFIG_PPC
+#      define XZ_DEC_POWERPC
+#endif
+#ifdef CONFIG_ARM
+#      define XZ_DEC_ARM
+#endif
+#ifdef CONFIG_IA64
+#      define XZ_DEC_IA64
+#endif
+#ifdef CONFIG_SPARC
+#      define XZ_DEC_SPARC
+#endif
+
+/*
+ * This will get the basic headers so that memeq() and others
+ * can be defined.
+ */
+#include "xz/xz_private.h"
+
+/*
+ * Replace the normal allocation functions with the versions from
+ * <linux/decompress/mm.h>. vfree() needs to support vfree(NULL)
+ * when XZ_DYNALLOC is used, but the pre-boot free() doesn't support it.
+ * Workaround it here because the other decompressors don't need it.
+ */
+#undef kmalloc
+#undef kfree
+#undef vmalloc
+#undef vfree
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) do { if (ptr != NULL) free(ptr); } while (0)
+
+/*
+ * FIXME: Not all basic memory functions are provided in architecture-specific
+ * files (yet). We define our own versions here for now, but this should be
+ * only a temporary solution.
+ *
+ * memeq and memzero are not used much and any remotely sane implementation
+ * is fast enough. memcpy/memmove speed matters in multi-call mode, but
+ * the kernel image is decompressed in single-call mode, in which only
+ * memcpy speed can matter and only if there is a lot of uncompressible data
+ * (LZMA2 stores uncompressible chunks in uncompressed form). Thus, the
+ * functions below should just be kept small; it's probably not worth
+ * optimizing for speed.
+ */
+
+#ifndef memeq
+static bool memeq(const void *a, const void *b, size_t size)
+{
+       const uint8_t *x = a;
+       const uint8_t *y = b;
+       size_t i;
+
+       for (i = 0; i < size; ++i)
+               if (x[i] != y[i])
+                       return false;
+
+       return true;
+}
+#endif
+
+#ifndef memzero
+static void memzero(void *buf, size_t size)
+{
+       uint8_t *b = buf;
+       uint8_t *e = b + size;
+
+       while (b != e)
+               *b++ = '\0';
+}
+#endif
+
+#if 0
+/* Not static to avoid a conflict with the prototype in the Linux headers. */
+void *memmove(void *dest, const void *src, size_t size)
+{
+       uint8_t *d = dest;
+       const uint8_t *s = src;
+       size_t i;
+
+       if (d < s) {
+               for (i = 0; i < size; ++i)
+                       d[i] = s[i];
+       } else if (d > s) {
+               i = size;
+               while (i-- > 0)
+                       d[i] = s[i];
+       }
+
+       return dest;
+}
+#endif
+
+/*
+ * Since we need memmove anyway, would use it as memcpy too.
+ * Commented out for now to avoid breaking things.
+ */
+/*
+#ifndef memcpy
+#      define memcpy memmove
+#endif
+*/
+
+#include "xz/xz_crc32.c"
+#include "xz/xz_dec_stream.c"
+#include "xz/xz_dec_lzma2.c"
+#include "xz/xz_dec_bcj.c"
+
+#endif /* XZ_PREBOOT */
+
+/* Size of the input and output buffers in multi-call mode */
+#define XZ_IOBUF_SIZE 4096
+
+/*
+ * This function implements the API defined in <linux/decompress/generic.h>.
+ *
+ * This wrapper will automatically choose single-call or multi-call mode
+ * of the native XZ decoder API. The single-call mode can be used only when
+ * both input and output buffers are available as a single chunk, i.e. when
+ * fill() and flush() won't be used.
+ */
+int INIT unxz(unsigned char *in, int in_size,
+                    int (*fill)(void *dest, unsigned int size),
+                    int (*flush)(void *src, unsigned int size),
+                    unsigned char *out, int *in_used,
+                    void (*error)(char *x))
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       bool must_free_in = false;
+
+#if XZ_INTERNAL_CRC32
+       xz_crc32_init();
+#endif
+
+       if (in_used != NULL)
+               *in_used = 0;
+
+       if (fill == NULL && flush == NULL)
+               s = xz_dec_init(XZ_SINGLE, 0);
+       else
+               s = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+
+       if (s == NULL)
+               goto error_alloc_state;
+
+       if (flush == NULL) {
+               b.out = out;
+               b.out_size = (size_t)-1;
+       } else {
+               b.out_size = XZ_IOBUF_SIZE;
+               b.out = malloc(XZ_IOBUF_SIZE);
+               if (b.out == NULL)
+                       goto error_alloc_out;
+       }
+
+       if (in == NULL) {
+               must_free_in = true;
+               in = malloc(XZ_IOBUF_SIZE);
+               if (in == NULL)
+                       goto error_alloc_in;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = in_size;
+       b.out_pos = 0;
+
+       if (fill == NULL && flush == NULL) {
+               ret = xz_dec_run(s, &b);
+       } else {
+               do {
+                       if (b.in_pos == b.in_size && fill != NULL) {
+                               if (in_used != NULL)
+                                       *in_used += b.in_pos;
+
+                               b.in_pos = 0;
+
+                               in_size = fill(in, XZ_IOBUF_SIZE);
+                               if (in_size < 0) {
+                                       /*
+                                        * This isn't an optimal error code
+                                        * but it probably isn't worth making
+                                        * a new one either.
+                                        */
+                                       ret = XZ_BUF_ERROR;
+                                       break;
+                               }
+
+                               b.in_size = in_size;
+                       }
+
+                       ret = xz_dec_run(s, &b);
+
+                       if (flush != NULL && (b.out_pos == b.out_size
+                                       || (ret != XZ_OK && b.out_pos > 0))) {
+                               /*
+                                * Setting ret here may hide an error
+                                * returned by xz_dec_run(), but probably
+                                * it's not too bad.
+                                */
+                               if (flush(b.out, b.out_pos) != (int)b.out_pos)
+                                       ret = XZ_BUF_ERROR;
+
+                               b.out_pos = 0;
+                       }
+               } while (ret == XZ_OK);
+
+               if (must_free_in)
+                       free(in);
+
+               if (flush != NULL)
+                       free(b.out);
+       }
+
+       if (in_used != NULL)
+               *in_used += b.in_pos;
+
+       xz_dec_end(s);
+
+       switch (ret) {
+       case XZ_STREAM_END:
+               return 0;
+
+       case XZ_MEM_ERROR:
+               /* This can occur only in multi-call mode. */
+               error("XZ decompressor ran out of memory");
+               break;
+
+       case XZ_FORMAT_ERROR:
+               error("Input is not in the XZ format (wrong magic bytes)");
+               break;
+
+       case XZ_OPTIONS_ERROR:
+               error("Input was encoded with settings that are not "
+                               "supported by this XZ decoder");
+               break;
+
+       case XZ_DATA_ERROR:
+       case XZ_BUF_ERROR:
+               error("XZ-compressed data is corrupt");
+               break;
+
+       default:
+               error("Bug in the XZ decompressor");
+               break;
+       }
+
+       return -1;
+
+error_alloc_in:
+       if (flush != NULL)
+               free(b.out);
+
+error_alloc_out:
+       xz_dec_end(s);
+
+error_alloc_state:
+       error("XZ decompressor ran out of memory");
+       return -1;
+}
+
+/*
+ * This macro is used by architecture-specific files to decompress
+ * the kernel image.
+ */
+#define decompress unxz
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Kconfig b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Kconfig
new file mode 100644 (file)
index 0000000..08837db
--- /dev/null
@@ -0,0 +1,57 @@
+config XZ_DEC
+       tristate "XZ decompression support"
+       select CRC32
+       help
+         LZMA2 compression algorithm and BCJ filters are supported using
+         the .xz file format as the container. For integrity checking,
+         CRC32 is supported. See Documentation/xz.txt for more information.
+
+if XZ_DEC
+
+config XZ_DEC_X86
+       bool "x86 BCJ filter decoder"
+       default y if X86
+       select XZ_DEC_BCJ
+
+config XZ_DEC_POWERPC
+       bool "PowerPC BCJ filter decoder"
+       default y if PPC
+       select XZ_DEC_BCJ
+
+config XZ_DEC_IA64
+       bool "IA-64 BCJ filter decoder"
+       default y if IA64
+       select XZ_DEC_BCJ
+
+config XZ_DEC_ARM
+       bool "ARM BCJ filter decoder"
+       default y if ARM
+       select XZ_DEC_BCJ
+
+config XZ_DEC_ARMTHUMB
+       bool "ARM-Thumb BCJ filter decoder"
+       default y if (ARM && ARM_THUMB)
+       select XZ_DEC_BCJ
+
+config XZ_DEC_SPARC
+       bool "SPARC BCJ filter decoder"
+       default y if SPARC
+       select XZ_DEC_BCJ
+
+endif
+
+config XZ_DEC_BCJ
+       bool
+       default n
+
+config XZ_DEC_TEST
+       tristate "XZ decompressor tester"
+       default n
+       depends on XZ_DEC
+       help
+         This allows passing .xz files to the in-kernel XZ decoder via
+         a character special file. It calculates CRC32 of the decompressed
+         data and writes diagnostics to the system log.
+
+         Unless you are developing the XZ decoder, you don't need this
+         and should say N.
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Makefile b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/Makefile
new file mode 100644 (file)
index 0000000..a7fa769
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_XZ_DEC) += xz_dec.o
+xz_dec-y := xz_dec_syms.o xz_dec_stream.o xz_dec_lzma2.o
+xz_dec-$(CONFIG_XZ_DEC_BCJ) += xz_dec_bcj.o
+
+obj-$(CONFIG_XZ_DEC_TEST) += xz_dec_test.o
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc32.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc32.c
new file mode 100644 (file)
index 0000000..34532d1
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * CRC32 using the polynomial from IEEE-802.3
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is not the fastest implementation, but it is pretty compact.
+ * The fastest versions of xz_crc32() on modern CPUs without hardware
+ * accelerated CRC instruction are 3-5 times as fast as this version,
+ * but they are bigger and use more memory for the lookup table.
+ */
+
+#include "xz_private.h"
+
+/*
+ * STATIC_RW_DATA is used in the pre-boot environment on some architectures.
+ * See <linux/decompress/mm.h> for details.
+ */
+#ifndef STATIC_RW_DATA
+#      define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint32_t xz_crc32_table[256];
+
+XZ_EXTERN void xz_crc32_init(void)
+{
+       const uint32_t poly = 0xEDB88320;
+
+       uint32_t i;
+       uint32_t j;
+       uint32_t r;
+
+       for (i = 0; i < 256; ++i) {
+               r = i;
+               for (j = 0; j < 8; ++j)
+                       r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+               xz_crc32_table[i] = r;
+       }
+
+       return;
+}
+
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+       crc = ~crc;
+
+       while (size != 0) {
+               crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+               --size;
+       }
+
+       return ~crc;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc64.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_crc64.c
new file mode 100644 (file)
index 0000000..ca1caee
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * CRC64 using the polynomial from ECMA-182
+ *
+ * This file is similar to xz_crc32.c. See the comments there.
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+#ifndef STATIC_RW_DATA
+#      define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint64_t xz_crc64_table[256];
+
+XZ_EXTERN void xz_crc64_init(void)
+{
+       const uint64_t poly = 0xC96C5795D7870F42;
+
+       uint32_t i;
+       uint32_t j;
+       uint64_t r;
+
+       for (i = 0; i < 256; ++i) {
+               r = i;
+               for (j = 0; j < 8; ++j)
+                       r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+               xz_crc64_table[i] = r;
+       }
+
+       return;
+}
+
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc)
+{
+       crc = ~crc;
+
+       while (size != 0) {
+               crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+               --size;
+       }
+
+       return ~crc;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_bcj.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_bcj.c
new file mode 100644 (file)
index 0000000..a768e6d
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+       /* Type of the BCJ filter being used */
+       enum {
+               BCJ_X86 = 4,        /* x86 or x86-64 */
+               BCJ_POWERPC = 5,    /* Big endian only */
+               BCJ_IA64 = 6,       /* Big or little endian */
+               BCJ_ARM = 7,        /* Little endian only */
+               BCJ_ARMTHUMB = 8,   /* Little endian only */
+               BCJ_SPARC = 9       /* Big or little endian */
+       } type;
+
+       /*
+        * Return value of the next filter in the chain. We need to preserve
+        * this information across calls, because we must not call the next
+        * filter anymore once it has returned XZ_STREAM_END.
+        */
+       enum xz_ret ret;
+
+       /* True if we are operating in single-call mode. */
+       bool single_call;
+
+       /*
+        * Absolute position relative to the beginning of the uncompressed
+        * data (in a single .xz Block). We care only about the lowest 32
+        * bits so this doesn't need to be uint64_t even with big files.
+        */
+       uint32_t pos;
+
+       /* x86 filter state */
+       uint32_t x86_prev_mask;
+
+       /* Temporary space to hold the variables from struct xz_buf */
+       uint8_t *out;
+       size_t out_pos;
+       size_t out_size;
+
+       struct {
+               /* Amount of already filtered data in the beginning of buf */
+               size_t filtered;
+
+               /* Total amount of data currently stored in buf  */
+               size_t size;
+
+               /*
+                * Buffer to hold a mix of filtered and unfiltered data. This
+                * needs to be big enough to hold Alignment + 2 * Look-ahead:
+                *
+                * Type         Alignment   Look-ahead
+                * x86              1           4
+                * PowerPC          4           0
+                * IA-64           16           0
+                * ARM              4           0
+                * ARM-Thumb        2           2
+                * SPARC            4           0
+                */
+               uint8_t buf[16];
+       } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+       return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       static const bool mask_to_allowed_status[8]
+               = { true, true, true, false, true, false, false, false };
+
+       static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+       size_t i;
+       size_t prev_pos = (size_t)-1;
+       uint32_t prev_mask = s->x86_prev_mask;
+       uint32_t src;
+       uint32_t dest;
+       uint32_t j;
+       uint8_t b;
+
+       if (size <= 4)
+               return 0;
+
+       size -= 4;
+       for (i = 0; i < size; ++i) {
+               if ((buf[i] & 0xFE) != 0xE8)
+                       continue;
+
+               prev_pos = i - prev_pos;
+               if (prev_pos > 3) {
+                       prev_mask = 0;
+               } else {
+                       prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+                       if (prev_mask != 0) {
+                               b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+                               if (!mask_to_allowed_status[prev_mask]
+                                               || bcj_x86_test_msbyte(b)) {
+                                       prev_pos = i;
+                                       prev_mask = (prev_mask << 1) | 1;
+                                       continue;
+                               }
+                       }
+               }
+
+               prev_pos = i;
+
+               if (bcj_x86_test_msbyte(buf[i + 4])) {
+                       src = get_unaligned_le32(buf + i + 1);
+                       while (true) {
+                               dest = src - (s->pos + (uint32_t)i + 5);
+                               if (prev_mask == 0)
+                                       break;
+
+                               j = mask_to_bit_num[prev_mask] * 8;
+                               b = (uint8_t)(dest >> (24 - j));
+                               if (!bcj_x86_test_msbyte(b))
+                                       break;
+
+                               src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+                       }
+
+                       dest &= 0x01FFFFFF;
+                       dest |= (uint32_t)0 - (dest & 0x01000000);
+                       put_unaligned_le32(dest, buf + i + 1);
+                       i += 4;
+               } else {
+                       prev_mask = (prev_mask << 1) | 1;
+               }
+       }
+
+       prev_pos = i - prev_pos;
+       s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t instr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               instr = get_unaligned_be32(buf + i);
+               if ((instr & 0xFC000003) == 0x48000001) {
+                       instr &= 0x03FFFFFC;
+                       instr -= s->pos + (uint32_t)i;
+                       instr &= 0x03FFFFFC;
+                       instr |= 0x48000001;
+                       put_unaligned_be32(instr, buf + i);
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       static const uint8_t branch_table[32] = {
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               4, 4, 6, 6, 0, 0, 7, 7,
+               4, 4, 0, 0, 4, 4, 0, 0
+       };
+
+       /*
+        * The local variables take a little bit stack space, but it's less
+        * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+        * stack usage here without doing that for the LZMA2 decoder too.
+        */
+
+       /* Loop counters */
+       size_t i;
+       size_t j;
+
+       /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+       uint32_t slot;
+
+       /* Bitwise offset of the instruction indicated by slot */
+       uint32_t bit_pos;
+
+       /* bit_pos split into byte and bit parts */
+       uint32_t byte_pos;
+       uint32_t bit_res;
+
+       /* Address part of an instruction */
+       uint32_t addr;
+
+       /* Mask used to detect which instructions to convert */
+       uint32_t mask;
+
+       /* 41-bit instruction stored somewhere in the lowest 48 bits */
+       uint64_t instr;
+
+       /* Instruction normalized with bit_res for easier manipulation */
+       uint64_t norm;
+
+       for (i = 0; i + 16 <= size; i += 16) {
+               mask = branch_table[buf[i] & 0x1F];
+               for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+                       if (((mask >> slot) & 1) == 0)
+                               continue;
+
+                       byte_pos = bit_pos >> 3;
+                       bit_res = bit_pos & 7;
+                       instr = 0;
+                       for (j = 0; j < 6; ++j)
+                               instr |= (uint64_t)(buf[i + j + byte_pos])
+                                               << (8 * j);
+
+                       norm = instr >> bit_res;
+
+                       if (((norm >> 37) & 0x0F) == 0x05
+                                       && ((norm >> 9) & 0x07) == 0) {
+                               addr = (norm >> 13) & 0x0FFFFF;
+                               addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+                               addr <<= 4;
+                               addr -= s->pos + (uint32_t)i;
+                               addr >>= 4;
+
+                               norm &= ~((uint64_t)0x8FFFFF << 13);
+                               norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+                               norm |= (uint64_t)(addr & 0x100000)
+                                               << (36 - 20);
+
+                               instr &= (1 << bit_res) - 1;
+                               instr |= norm << bit_res;
+
+                               for (j = 0; j < 6; j++)
+                                       buf[i + j + byte_pos]
+                                               = (uint8_t)(instr >> (8 * j));
+                       }
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t addr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               if (buf[i + 3] == 0xEB) {
+                       addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+                                       | ((uint32_t)buf[i + 2] << 16);
+                       addr <<= 2;
+                       addr -= s->pos + (uint32_t)i + 8;
+                       addr >>= 2;
+                       buf[i] = (uint8_t)addr;
+                       buf[i + 1] = (uint8_t)(addr >> 8);
+                       buf[i + 2] = (uint8_t)(addr >> 16);
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t addr;
+
+       for (i = 0; i + 4 <= size; i += 2) {
+               if ((buf[i + 1] & 0xF8) == 0xF0
+                               && (buf[i + 3] & 0xF8) == 0xF8) {
+                       addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+                                       | ((uint32_t)buf[i] << 11)
+                                       | (((uint32_t)buf[i + 3] & 0x07) << 8)
+                                       | (uint32_t)buf[i + 2];
+                       addr <<= 1;
+                       addr -= s->pos + (uint32_t)i + 4;
+                       addr >>= 1;
+                       buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+                       buf[i] = (uint8_t)(addr >> 11);
+                       buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+                       buf[i + 2] = (uint8_t)addr;
+                       i += 2;
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t instr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               instr = get_unaligned_be32(buf + i);
+               if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+                       instr <<= 2;
+                       instr -= s->pos + (uint32_t)i;
+                       instr >>= 2;
+                       instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+                                       | 0x40000000 | (instr & 0x3FFFFF);
+                       put_unaligned_be32(instr, buf + i);
+               }
+       }
+
+       return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s,
+                     uint8_t *buf, size_t *pos, size_t size)
+{
+       size_t filtered;
+
+       buf += *pos;
+       size -= *pos;
+
+       switch (s->type) {
+#ifdef XZ_DEC_X86
+       case BCJ_X86:
+               filtered = bcj_x86(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_POWERPC
+       case BCJ_POWERPC:
+               filtered = bcj_powerpc(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_IA64
+       case BCJ_IA64:
+               filtered = bcj_ia64(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_ARM
+       case BCJ_ARM:
+               filtered = bcj_arm(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+       case BCJ_ARMTHUMB:
+               filtered = bcj_armthumb(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_SPARC
+       case BCJ_SPARC:
+               filtered = bcj_sparc(s, buf, size);
+               break;
+#endif
+       default:
+               /* Never reached but silence compiler warnings. */
+               filtered = 0;
+               break;
+       }
+
+       *pos += filtered;
+       s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+       size_t copy_size;
+
+       copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+       memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+       b->out_pos += copy_size;
+
+       s->temp.filtered -= copy_size;
+       s->temp.size -= copy_size;
+       memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+                                    struct xz_dec_lzma2 *lzma2,
+                                    struct xz_buf *b)
+{
+       size_t out_start;
+
+       /*
+        * Flush pending already filtered data to the output buffer. Return
+        * immediatelly if we couldn't flush everything, or if the next
+        * filter in the chain had already returned XZ_STREAM_END.
+        */
+       if (s->temp.filtered > 0) {
+               bcj_flush(s, b);
+               if (s->temp.filtered > 0)
+                       return XZ_OK;
+
+               if (s->ret == XZ_STREAM_END)
+                       return XZ_STREAM_END;
+       }
+
+       /*
+        * If we have more output space than what is currently pending in
+        * temp, copy the unfiltered data from temp to the output buffer
+        * and try to fill the output buffer by decoding more data from the
+        * next filter in the chain. Apply the BCJ filter on the new data
+        * in the output buffer. If everything cannot be filtered, copy it
+        * to temp and rewind the output buffer position accordingly.
+        *
+        * This needs to be always run when temp.size == 0 to handle a special
+        * case where the output buffer is full and the next filter has no
+        * more output coming but hasn't returned XZ_STREAM_END yet.
+        */
+       if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
+               out_start = b->out_pos;
+               memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+               b->out_pos += s->temp.size;
+
+               s->ret = xz_dec_lzma2_run(lzma2, b);
+               if (s->ret != XZ_STREAM_END
+                               && (s->ret != XZ_OK || s->single_call))
+                       return s->ret;
+
+               bcj_apply(s, b->out, &out_start, b->out_pos);
+
+               /*
+                * As an exception, if the next filter returned XZ_STREAM_END,
+                * we can do that too, since the last few bytes that remain
+                * unfiltered are meant to remain unfiltered.
+                */
+               if (s->ret == XZ_STREAM_END)
+                       return XZ_STREAM_END;
+
+               s->temp.size = b->out_pos - out_start;
+               b->out_pos -= s->temp.size;
+               memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+               /*
+                * If there wasn't enough input to the next filter to fill
+                * the output buffer with unfiltered data, there's no point
+                * to try decoding more data to temp.
+                */
+               if (b->out_pos + s->temp.size < b->out_size)
+                       return XZ_OK;
+       }
+
+       /*
+        * We have unfiltered data in temp. If the output buffer isn't full
+        * yet, try to fill the temp buffer by decoding more data from the
+        * next filter. Apply the BCJ filter on temp. Then we hopefully can
+        * fill the actual output buffer by copying filtered data from temp.
+        * A mix of filtered and unfiltered data may be left in temp; it will
+        * be taken care on the next call to this function.
+        */
+       if (b->out_pos < b->out_size) {
+               /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+               s->out = b->out;
+               s->out_pos = b->out_pos;
+               s->out_size = b->out_size;
+               b->out = s->temp.buf;
+               b->out_pos = s->temp.size;
+               b->out_size = sizeof(s->temp.buf);
+
+               s->ret = xz_dec_lzma2_run(lzma2, b);
+
+               s->temp.size = b->out_pos;
+               b->out = s->out;
+               b->out_pos = s->out_pos;
+               b->out_size = s->out_size;
+
+               if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+                       return s->ret;
+
+               bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+               /*
+                * If the next filter returned XZ_STREAM_END, we mark that
+                * everything is filtered, since the last unfiltered bytes
+                * of the stream are meant to be left as is.
+                */
+               if (s->ret == XZ_STREAM_END)
+                       s->temp.filtered = s->temp.size;
+
+               bcj_flush(s, b);
+               if (s->temp.filtered > 0)
+                       return XZ_OK;
+       }
+
+       return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+{
+       struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s != NULL)
+               s->single_call = single_call;
+
+       return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+       switch (id) {
+#ifdef XZ_DEC_X86
+       case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+       case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+       case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+       case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+       case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+       case BCJ_SPARC:
+#endif
+               break;
+
+       default:
+               /* Unsupported Filter ID */
+               return XZ_OPTIONS_ERROR;
+       }
+
+       s->type = id;
+       s->ret = XZ_OK;
+       s->pos = 0;
+       s->x86_prev_mask = 0;
+       s->temp.filtered = 0;
+       s->temp.size = 0;
+
+       return XZ_OK;
+}
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c
new file mode 100644 (file)
index 0000000..a6cdc96
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ *    start <= pos <= full <= end
+ *    pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ *    end == size
+ *    size <= size_max
+ *    allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+       /* Beginning of the history buffer */
+       uint8_t *buf;
+
+       /* Old position in buf (before decoding more data) */
+       size_t start;
+
+       /* Position in buf */
+       size_t pos;
+
+       /*
+        * How full dictionary is. This is used to detect corrupt input that
+        * would read beyond the beginning of the uncompressed stream.
+        */
+       size_t full;
+
+       /* Write limit; we don't write to buf[limit] or later bytes. */
+       size_t limit;
+
+       /*
+        * End of the dictionary buffer. In multi-call mode, this is
+        * the same as the dictionary size. In single-call mode, this
+        * indicates the size of the output buffer.
+        */
+       size_t end;
+
+       /*
+        * Size of the dictionary as specified in Block Header. This is used
+        * together with "full" to detect corrupt input that would make us
+        * read beyond the beginning of the uncompressed stream.
+        */
+       uint32_t size;
+
+       /*
+        * Maximum allowed dictionary size in multi-call mode.
+        * This is ignored in single-call mode.
+        */
+       uint32_t size_max;
+
+       /*
+        * Amount of memory currently allocated for the dictionary.
+        * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+        * size_max is always the same as the allocated size.)
+        */
+       uint32_t allocated;
+
+       /* Operation mode */
+       enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+       uint32_t range;
+       uint32_t code;
+
+       /*
+        * Number of initializing bytes remaining to be read
+        * by rc_read_init().
+        */
+       uint32_t init_bytes_left;
+
+       /*
+        * Buffer from which we read our input. It can be either
+        * temp.buf or the caller-provided input buffer.
+        */
+       const uint8_t *in;
+       size_t in_pos;
+       size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+       /* Probability of match length being at least 10 */
+       uint16_t choice;
+
+       /* Probability of match length being at least 18 */
+       uint16_t choice2;
+
+       /* Probabilities for match lengths 2-9 */
+       uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+       /* Probabilities for match lengths 10-17 */
+       uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+       /* Probabilities for match lengths 18-273 */
+       uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+       /* Distances of latest four matches */
+       uint32_t rep0;
+       uint32_t rep1;
+       uint32_t rep2;
+       uint32_t rep3;
+
+       /* Types of the most recently seen LZMA symbols */
+       enum lzma_state state;
+
+       /*
+        * Length of a match. This is updated so that dict_repeat can
+        * be called again to finish repeating the whole match.
+        */
+       uint32_t len;
+
+       /*
+        * LZMA properties or related bit masks (number of literal
+        * context bits, a mask dervied from the number of literal
+        * position bits, and a mask dervied from the number
+        * position bits)
+        */
+       uint32_t lc;
+       uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+       uint32_t pos_mask;         /* (1 << pb) - 1 */
+
+       /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+       uint16_t is_match[STATES][POS_STATES_MAX];
+
+       /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+       uint16_t is_rep[STATES];
+
+       /*
+        * If 0, distance of a repeated match is rep0.
+        * Otherwise check is_rep1.
+        */
+       uint16_t is_rep0[STATES];
+
+       /*
+        * If 0, distance of a repeated match is rep1.
+        * Otherwise check is_rep2.
+        */
+       uint16_t is_rep1[STATES];
+
+       /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+       uint16_t is_rep2[STATES];
+
+       /*
+        * If 1, the repeated match has length of one byte. Otherwise
+        * the length is decoded from rep_len_decoder.
+        */
+       uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+       /*
+        * Probability tree for the highest two bits of the match
+        * distance. There is a separate probability tree for match
+        * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+        */
+       uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+       /*
+        * Probility trees for additional bits for match distance
+        * when the distance is in the range [4, 127].
+        */
+       uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+       /*
+        * Probability tree for the lowest four bits of a match
+        * distance that is equal to or greater than 128.
+        */
+       uint16_t dist_align[ALIGN_SIZE];
+
+       /* Length of a normal match */
+       struct lzma_len_dec match_len_dec;
+
+       /* Length of a repeated match */
+       struct lzma_len_dec rep_len_dec;
+
+       /* Probabilities of literals */
+       uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+       /* Position in xz_dec_lzma2_run(). */
+       enum lzma2_seq {
+               SEQ_CONTROL,
+               SEQ_UNCOMPRESSED_1,
+               SEQ_UNCOMPRESSED_2,
+               SEQ_COMPRESSED_0,
+               SEQ_COMPRESSED_1,
+               SEQ_PROPERTIES,
+               SEQ_LZMA_PREPARE,
+               SEQ_LZMA_RUN,
+               SEQ_COPY
+       } sequence;
+
+       /* Next position after decoding the compressed size of the chunk. */
+       enum lzma2_seq next_sequence;
+
+       /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+       uint32_t uncompressed;
+
+       /*
+        * Compressed size of LZMA chunk or compressed/uncompressed
+        * size of uncompressed chunk (64 KiB at maximum)
+        */
+       uint32_t compressed;
+
+       /*
+        * True if dictionary reset is needed. This is false before
+        * the first chunk (LZMA or uncompressed).
+        */
+       bool need_dict_reset;
+
+       /*
+        * True if new LZMA properties are needed. This is false
+        * before the first LZMA chunk.
+        */
+       bool need_props;
+};
+
+struct xz_dec_lzma2 {
+       /*
+        * The order below is important on x86 to reduce code size and
+        * it shouldn't hurt on other platforms. Everything up to and
+        * including lzma.pos_mask are in the first 128 bytes on x86-32,
+        * which allows using smaller instructions to access those
+        * variables. On x86-64, fewer variables fit into the first 128
+        * bytes, but this is still the best order without sacrificing
+        * the readability by splitting the structures.
+        */
+       struct rc_dec rc;
+       struct dictionary dict;
+       struct lzma2_dec lzma2;
+       struct lzma_dec lzma;
+
+       /*
+        * Temporary buffer which holds small number of input bytes between
+        * decoder calls. See lzma2_lzma() for details.
+        */
+       struct {
+               uint32_t size;
+               uint8_t buf[3 * LZMA_IN_REQUIRED];
+       } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+       if (DEC_IS_SINGLE(dict->mode)) {
+               dict->buf = b->out + b->out_pos;
+               dict->end = b->out_size - b->out_pos;
+       }
+
+       dict->start = 0;
+       dict->pos = 0;
+       dict->limit = 0;
+       dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+       if (dict->end - dict->pos <= out_max)
+               dict->limit = dict->end;
+       else
+               dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline bool dict_has_space(const struct dictionary *dict)
+{
+       return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+       size_t offset = dict->pos - dist - 1;
+
+       if (dist >= dict->pos)
+               offset += dict->end;
+
+       return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+       dict->buf[dict->pos++] = byte;
+
+       if (dict->full < dict->pos)
+               dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+       size_t back;
+       uint32_t left;
+
+       if (dist >= dict->full || dist >= dict->size)
+               return false;
+
+       left = min_t(size_t, dict->limit - dict->pos, *len);
+       *len -= left;
+
+       back = dict->pos - dist - 1;
+       if (dist >= dict->pos)
+               back += dict->end;
+
+       do {
+               dict->buf[dict->pos++] = dict->buf[back++];
+               if (back == dict->end)
+                       back = 0;
+       } while (--left > 0);
+
+       if (dict->full < dict->pos)
+               dict->full = dict->pos;
+
+       return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,
+                             uint32_t *left)
+{
+       size_t copy_size;
+
+       while (*left > 0 && b->in_pos < b->in_size
+                       && b->out_pos < b->out_size) {
+               copy_size = min(b->in_size - b->in_pos,
+                               b->out_size - b->out_pos);
+               if (copy_size > dict->end - dict->pos)
+                       copy_size = dict->end - dict->pos;
+               if (copy_size > *left)
+                       copy_size = *left;
+
+               *left -= copy_size;
+
+               memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+               dict->pos += copy_size;
+
+               if (dict->full < dict->pos)
+                       dict->full = dict->pos;
+
+               if (DEC_IS_MULTI(dict->mode)) {
+                       if (dict->pos == dict->end)
+                               dict->pos = 0;
+
+                       memcpy(b->out + b->out_pos, b->in + b->in_pos,
+                                       copy_size);
+               }
+
+               dict->start = dict->pos;
+
+               b->out_pos += copy_size;
+               b->in_pos += copy_size;
+       }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+       size_t copy_size = dict->pos - dict->start;
+
+       if (DEC_IS_MULTI(dict->mode)) {
+               if (dict->pos == dict->end)
+                       dict->pos = 0;
+
+               memcpy(b->out + b->out_pos, dict->buf + dict->start,
+                               copy_size);
+       }
+
+       dict->start = dict->pos;
+       b->out_pos += copy_size;
+       return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+       rc->range = (uint32_t)-1;
+       rc->code = 0;
+       rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+       while (rc->init_bytes_left > 0) {
+               if (b->in_pos == b->in_size)
+                       return false;
+
+               rc->code = (rc->code << 8) + b->in[b->in_pos++];
+               --rc->init_bytes_left;
+       }
+
+       return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool rc_limit_exceeded(const struct rc_dec *rc)
+{
+       return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool rc_is_finished(const struct rc_dec *rc)
+{
+       return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void rc_normalize(struct rc_dec *rc)
+{
+       if (rc->range < RC_TOP_VALUE) {
+               rc->range <<= RC_SHIFT_BITS;
+               rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+       }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+       uint32_t bound;
+       int bit;
+
+       rc_normalize(rc);
+       bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+       if (rc->code < bound) {
+               rc->range = bound;
+               *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+               bit = 0;
+       } else {
+               rc->range -= bound;
+               rc->code -= bound;
+               *prob -= *prob >> RC_MOVE_BITS;
+               bit = 1;
+       }
+
+       return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t rc_bittree(struct rc_dec *rc,
+                                          uint16_t *probs, uint32_t limit)
+{
+       uint32_t symbol = 1;
+
+       do {
+               if (rc_bit(rc, &probs[symbol]))
+                       symbol = (symbol << 1) + 1;
+               else
+                       symbol <<= 1;
+       } while (symbol < limit);
+
+       return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void rc_bittree_reverse(struct rc_dec *rc,
+                                              uint16_t *probs,
+                                              uint32_t *dest, uint32_t limit)
+{
+       uint32_t symbol = 1;
+       uint32_t i = 0;
+
+       do {
+               if (rc_bit(rc, &probs[symbol])) {
+                       symbol = (symbol << 1) + 1;
+                       *dest += 1 << i;
+               } else {
+                       symbol <<= 1;
+               }
+       } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+       uint32_t mask;
+
+       do {
+               rc_normalize(rc);
+               rc->range >>= 1;
+               rc->code -= rc->range;
+               mask = (uint32_t)0 - (rc->code >> 31);
+               rc->code += rc->range & mask;
+               *dest = (*dest << 1) + (mask + 1);
+       } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+       uint32_t prev_byte = dict_get(&s->dict, 0);
+       uint32_t low = prev_byte >> (8 - s->lzma.lc);
+       uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+       return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+       uint16_t *probs;
+       uint32_t symbol;
+       uint32_t match_byte;
+       uint32_t match_bit;
+       uint32_t offset;
+       uint32_t i;
+
+       probs = lzma_literal_probs(s);
+
+       if (lzma_state_is_literal(s->lzma.state)) {
+               symbol = rc_bittree(&s->rc, probs, 0x100);
+       } else {
+               symbol = 1;
+               match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+               offset = 0x100;
+
+               do {
+                       match_bit = match_byte & offset;
+                       match_byte <<= 1;
+                       i = offset + match_bit + symbol;
+
+                       if (rc_bit(&s->rc, &probs[i])) {
+                               symbol = (symbol << 1) + 1;
+                               offset &= match_bit;
+                       } else {
+                               symbol <<= 1;
+                               offset &= ~match_bit;
+                       }
+               } while (symbol < 0x100);
+       }
+
+       dict_put(&s->dict, (uint8_t)symbol);
+       lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+                    uint32_t pos_state)
+{
+       uint16_t *probs;
+       uint32_t limit;
+
+       if (!rc_bit(&s->rc, &l->choice)) {
+               probs = l->low[pos_state];
+               limit = LEN_LOW_SYMBOLS;
+               s->lzma.len = MATCH_LEN_MIN;
+       } else {
+               if (!rc_bit(&s->rc, &l->choice2)) {
+                       probs = l->mid[pos_state];
+                       limit = LEN_MID_SYMBOLS;
+                       s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+               } else {
+                       probs = l->high;
+                       limit = LEN_HIGH_SYMBOLS;
+                       s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+                                       + LEN_MID_SYMBOLS;
+               }
+       }
+
+       s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+       uint16_t *probs;
+       uint32_t dist_slot;
+       uint32_t limit;
+
+       lzma_state_match(&s->lzma.state);
+
+       s->lzma.rep3 = s->lzma.rep2;
+       s->lzma.rep2 = s->lzma.rep1;
+       s->lzma.rep1 = s->lzma.rep0;
+
+       lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+       probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+       dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+       if (dist_slot < DIST_MODEL_START) {
+               s->lzma.rep0 = dist_slot;
+       } else {
+               limit = (dist_slot >> 1) - 1;
+               s->lzma.rep0 = 2 + (dist_slot & 1);
+
+               if (dist_slot < DIST_MODEL_END) {
+                       s->lzma.rep0 <<= limit;
+                       probs = s->lzma.dist_special + s->lzma.rep0
+                                       - dist_slot - 1;
+                       rc_bittree_reverse(&s->rc, probs,
+                                       &s->lzma.rep0, limit);
+               } else {
+                       rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+                       s->lzma.rep0 <<= ALIGN_BITS;
+                       rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+                                       &s->lzma.rep0, ALIGN_BITS);
+               }
+       }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+       uint32_t tmp;
+
+       if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+               if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+                               s->lzma.state][pos_state])) {
+                       lzma_state_short_rep(&s->lzma.state);
+                       s->lzma.len = 1;
+                       return;
+               }
+       } else {
+               if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+                       tmp = s->lzma.rep1;
+               } else {
+                       if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+                               tmp = s->lzma.rep2;
+                       } else {
+                               tmp = s->lzma.rep3;
+                               s->lzma.rep3 = s->lzma.rep2;
+                       }
+
+                       s->lzma.rep2 = s->lzma.rep1;
+               }
+
+               s->lzma.rep1 = s->lzma.rep0;
+               s->lzma.rep0 = tmp;
+       }
+
+       lzma_state_long_rep(&s->lzma.state);
+       lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool lzma_main(struct xz_dec_lzma2 *s)
+{
+       uint32_t pos_state;
+
+       /*
+        * If the dictionary was reached during the previous call, try to
+        * finish the possibly pending repeat in the dictionary.
+        */
+       if (dict_has_space(&s->dict) && s->lzma.len > 0)
+               dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+       /*
+        * Decode more LZMA symbols. One iteration may consume up to
+        * LZMA_IN_REQUIRED - 1 bytes.
+        */
+       while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+               pos_state = s->dict.pos & s->lzma.pos_mask;
+
+               if (!rc_bit(&s->rc, &s->lzma.is_match[
+                               s->lzma.state][pos_state])) {
+                       lzma_literal(s);
+               } else {
+                       if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+                               lzma_rep_match(s, pos_state);
+                       else
+                               lzma_match(s, pos_state);
+
+                       if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+                               return false;
+               }
+       }
+
+       /*
+        * Having the range decoder always normalized when we are outside
+        * this function makes it easier to correctly handle end of the chunk.
+        */
+       rc_normalize(&s->rc);
+
+       return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+       uint16_t *probs;
+       size_t i;
+
+       s->lzma.state = STATE_LIT_LIT;
+       s->lzma.rep0 = 0;
+       s->lzma.rep1 = 0;
+       s->lzma.rep2 = 0;
+       s->lzma.rep3 = 0;
+
+       /*
+        * All probabilities are initialized to the same value. This hack
+        * makes the code smaller by avoiding a separate loop for each
+        * probability array.
+        *
+        * This could be optimized so that only that part of literal
+        * probabilities that are actually required. In the common case
+        * we would write 12 KiB less.
+        */
+       probs = s->lzma.is_match[0];
+       for (i = 0; i < PROBS_TOTAL; ++i)
+               probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+       rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+       if (props > (4 * 5 + 4) * 9 + 8)
+               return false;
+
+       s->lzma.pos_mask = 0;
+       while (props >= 9 * 5) {
+               props -= 9 * 5;
+               ++s->lzma.pos_mask;
+       }
+
+       s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+       s->lzma.literal_pos_mask = 0;
+       while (props >= 9) {
+               props -= 9;
+               ++s->lzma.literal_pos_mask;
+       }
+
+       s->lzma.lc = props;
+
+       if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+               return false;
+
+       s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+       lzma_reset(s);
+
+       return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+       size_t in_avail;
+       uint32_t tmp;
+
+       in_avail = b->in_size - b->in_pos;
+       if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+               tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+               if (tmp > s->lzma2.compressed - s->temp.size)
+                       tmp = s->lzma2.compressed - s->temp.size;
+               if (tmp > in_avail)
+                       tmp = in_avail;
+
+               memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+               if (s->temp.size + tmp == s->lzma2.compressed) {
+                       memzero(s->temp.buf + s->temp.size + tmp,
+                                       sizeof(s->temp.buf)
+                                               - s->temp.size - tmp);
+                       s->rc.in_limit = s->temp.size + tmp;
+               } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+                       s->temp.size += tmp;
+                       b->in_pos += tmp;
+                       return true;
+               } else {
+                       s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+               }
+
+               s->rc.in = s->temp.buf;
+               s->rc.in_pos = 0;
+
+               if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+                       return false;
+
+               s->lzma2.compressed -= s->rc.in_pos;
+
+               if (s->rc.in_pos < s->temp.size) {
+                       s->temp.size -= s->rc.in_pos;
+                       memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+                                       s->temp.size);
+                       return true;
+               }
+
+               b->in_pos += s->rc.in_pos - s->temp.size;
+               s->temp.size = 0;
+       }
+
+       in_avail = b->in_size - b->in_pos;
+       if (in_avail >= LZMA_IN_REQUIRED) {
+               s->rc.in = b->in;
+               s->rc.in_pos = b->in_pos;
+
+               if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+                       s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+               else
+                       s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+               if (!lzma_main(s))
+                       return false;
+
+               in_avail = s->rc.in_pos - b->in_pos;
+               if (in_avail > s->lzma2.compressed)
+                       return false;
+
+               s->lzma2.compressed -= in_avail;
+               b->in_pos = s->rc.in_pos;
+       }
+
+       in_avail = b->in_size - b->in_pos;
+       if (in_avail < LZMA_IN_REQUIRED) {
+               if (in_avail > s->lzma2.compressed)
+                       in_avail = s->lzma2.compressed;
+
+               memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+               s->temp.size = in_avail;
+               b->in_pos += in_avail;
+       }
+
+       return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+                                      struct xz_buf *b)
+{
+       uint32_t tmp;
+
+       while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+               switch (s->lzma2.sequence) {
+               case SEQ_CONTROL:
+                       /*
+                        * LZMA2 control byte
+                        *
+                        * Exact values:
+                        *   0x00   End marker
+                        *   0x01   Dictionary reset followed by
+                        *          an uncompressed chunk
+                        *   0x02   Uncompressed chunk (no dictionary reset)
+                        *
+                        * Highest three bits (s->control & 0xE0):
+                        *   0xE0   Dictionary reset, new properties and state
+                        *          reset, followed by LZMA compressed chunk
+                        *   0xC0   New properties and state reset, followed
+                        *          by LZMA compressed chunk (no dictionary
+                        *          reset)
+                        *   0xA0   State reset using old properties,
+                        *          followed by LZMA compressed chunk (no
+                        *          dictionary reset)
+                        *   0x80   LZMA chunk (no dictionary or state reset)
+                        *
+                        * For LZMA compressed chunks, the lowest five bits
+                        * (s->control & 1F) are the highest bits of the
+                        * uncompressed size (bits 16-20).
+                        *
+                        * A new LZMA2 stream must begin with a dictionary
+                        * reset. The first LZMA chunk must set new
+                        * properties and reset the LZMA state.
+                        *
+                        * Values that don't match anything described above
+                        * are invalid and we return XZ_DATA_ERROR.
+                        */
+                       tmp = b->in[b->in_pos++];
+
+                       if (tmp == 0x00)
+                               return XZ_STREAM_END;
+
+                       if (tmp >= 0xE0 || tmp == 0x01) {
+                               s->lzma2.need_props = true;
+                               s->lzma2.need_dict_reset = false;
+                               dict_reset(&s->dict, b);
+                       } else if (s->lzma2.need_dict_reset) {
+                               return XZ_DATA_ERROR;
+                       }
+
+                       if (tmp >= 0x80) {
+                               s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+                               s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+                               if (tmp >= 0xC0) {
+                                       /*
+                                        * When there are new properties,
+                                        * state reset is done at
+                                        * SEQ_PROPERTIES.
+                                        */
+                                       s->lzma2.need_props = false;
+                                       s->lzma2.next_sequence
+                                                       = SEQ_PROPERTIES;
+
+                               } else if (s->lzma2.need_props) {
+                                       return XZ_DATA_ERROR;
+
+                               } else {
+                                       s->lzma2.next_sequence
+                                                       = SEQ_LZMA_PREPARE;
+                                       if (tmp >= 0xA0)
+                                               lzma_reset(s);
+                               }
+                       } else {
+                               if (tmp > 0x02)
+                                       return XZ_DATA_ERROR;
+
+                               s->lzma2.sequence = SEQ_COMPRESSED_0;
+                               s->lzma2.next_sequence = SEQ_COPY;
+                       }
+
+                       break;
+
+               case SEQ_UNCOMPRESSED_1:
+                       s->lzma2.uncompressed
+                                       += (uint32_t)b->in[b->in_pos++] << 8;
+                       s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+                       break;
+
+               case SEQ_UNCOMPRESSED_2:
+                       s->lzma2.uncompressed
+                                       += (uint32_t)b->in[b->in_pos++] + 1;
+                       s->lzma2.sequence = SEQ_COMPRESSED_0;
+                       break;
+
+               case SEQ_COMPRESSED_0:
+                       s->lzma2.compressed
+                                       = (uint32_t)b->in[b->in_pos++] << 8;
+                       s->lzma2.sequence = SEQ_COMPRESSED_1;
+                       break;
+
+               case SEQ_COMPRESSED_1:
+                       s->lzma2.compressed
+                                       += (uint32_t)b->in[b->in_pos++] + 1;
+                       s->lzma2.sequence = s->lzma2.next_sequence;
+                       break;
+
+               case SEQ_PROPERTIES:
+                       if (!lzma_props(s, b->in[b->in_pos++]))
+                               return XZ_DATA_ERROR;
+
+                       s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+               case SEQ_LZMA_PREPARE:
+                       if (s->lzma2.compressed < RC_INIT_BYTES)
+                               return XZ_DATA_ERROR;
+
+                       if (!rc_read_init(&s->rc, b))
+                               return XZ_OK;
+
+                       s->lzma2.compressed -= RC_INIT_BYTES;
+                       s->lzma2.sequence = SEQ_LZMA_RUN;
+
+               case SEQ_LZMA_RUN:
+                       /*
+                        * Set dictionary limit to indicate how much we want
+                        * to be encoded at maximum. Decode new data into the
+                        * dictionary. Flush the new data from dictionary to
+                        * b->out. Check if we finished decoding this chunk.
+                        * In case the dictionary got full but we didn't fill
+                        * the output buffer yet, we may run this loop
+                        * multiple times without changing s->lzma2.sequence.
+                        */
+                       dict_limit(&s->dict, min_t(size_t,
+                                       b->out_size - b->out_pos,
+                                       s->lzma2.uncompressed));
+                       if (!lzma2_lzma(s, b))
+                               return XZ_DATA_ERROR;
+
+                       s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+                       if (s->lzma2.uncompressed == 0) {
+                               if (s->lzma2.compressed > 0 || s->lzma.len > 0
+                                               || !rc_is_finished(&s->rc))
+                                       return XZ_DATA_ERROR;
+
+                               rc_reset(&s->rc);
+                               s->lzma2.sequence = SEQ_CONTROL;
+
+                       } else if (b->out_pos == b->out_size
+                                       || (b->in_pos == b->in_size
+                                               && s->temp.size
+                                               < s->lzma2.compressed)) {
+                               return XZ_OK;
+                       }
+
+                       break;
+
+               case SEQ_COPY:
+                       dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+                       if (s->lzma2.compressed > 0)
+                               return XZ_OK;
+
+                       s->lzma2.sequence = SEQ_CONTROL;
+                       break;
+               }
+       }
+
+       return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+                                                  uint32_t dict_max)
+{
+       struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s == NULL)
+               return NULL;
+
+       s->dict.mode = mode;
+       s->dict.size_max = dict_max;
+
+       if (DEC_IS_PREALLOC(mode)) {
+               s->dict.buf = vmalloc(dict_max);
+               if (s->dict.buf == NULL) {
+                       kfree(s);
+                       return NULL;
+               }
+       } else if (DEC_IS_DYNALLOC(mode)) {
+               s->dict.buf = NULL;
+               s->dict.allocated = 0;
+       }
+
+       return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+       /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+       if (props > 39)
+               return XZ_OPTIONS_ERROR;
+
+       s->dict.size = 2 + (props & 1);
+       s->dict.size <<= (props >> 1) + 11;
+
+       if (DEC_IS_MULTI(s->dict.mode)) {
+               if (s->dict.size > s->dict.size_max)
+                       return XZ_MEMLIMIT_ERROR;
+
+               s->dict.end = s->dict.size;
+
+               if (DEC_IS_DYNALLOC(s->dict.mode)) {
+                       if (s->dict.allocated < s->dict.size) {
+                               vfree(s->dict.buf);
+                               s->dict.buf = vmalloc(s->dict.size);
+                               if (s->dict.buf == NULL) {
+                                       s->dict.allocated = 0;
+                                       return XZ_MEM_ERROR;
+                               }
+                       }
+               }
+       }
+
+       s->lzma.len = 0;
+
+       s->lzma2.sequence = SEQ_CONTROL;
+       s->lzma2.need_dict_reset = true;
+
+       s->temp.size = 0;
+
+       return XZ_OK;
+}
+
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+       if (DEC_IS_MULTI(s->dict.mode))
+               vfree(s->dict.buf);
+
+       kfree(s);
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_stream.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_stream.c
new file mode 100644 (file)
index 0000000..d652550
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+#ifdef XZ_USE_CRC64
+#      define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
+#else
+#      define IS_CRC64(check_type) false
+#endif
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+       vli_type unpadded;
+       vli_type uncompressed;
+       uint32_t crc32;
+};
+
+struct xz_dec {
+       /* Position in dec_main() */
+       enum {
+               SEQ_STREAM_HEADER,
+               SEQ_BLOCK_START,
+               SEQ_BLOCK_HEADER,
+               SEQ_BLOCK_UNCOMPRESS,
+               SEQ_BLOCK_PADDING,
+               SEQ_BLOCK_CHECK,
+               SEQ_INDEX,
+               SEQ_INDEX_PADDING,
+               SEQ_INDEX_CRC32,
+               SEQ_STREAM_FOOTER
+       } sequence;
+
+       /* Position in variable-length integers and Check fields */
+       uint32_t pos;
+
+       /* Variable-length integer decoded by dec_vli() */
+       vli_type vli;
+
+       /* Saved in_pos and out_pos */
+       size_t in_start;
+       size_t out_start;
+
+#ifdef XZ_USE_CRC64
+       /* CRC32 or CRC64 value in Block or CRC32 value in Index */
+       uint64_t crc;
+#else
+       /* CRC32 value in Block or Index */
+       uint32_t crc;
+#endif
+
+       /* Type of the integrity check calculated from uncompressed data */
+       enum xz_check check_type;
+
+       /* Operation mode */
+       enum xz_mode mode;
+
+       /*
+        * True if the next call to xz_dec_run() is allowed to return
+        * XZ_BUF_ERROR.
+        */
+       bool allow_buf_error;
+
+       /* Information stored in Block Header */
+       struct {
+               /*
+                * Value stored in the Compressed Size field, or
+                * VLI_UNKNOWN if Compressed Size is not present.
+                */
+               vli_type compressed;
+
+               /*
+                * Value stored in the Uncompressed Size field, or
+                * VLI_UNKNOWN if Uncompressed Size is not present.
+                */
+               vli_type uncompressed;
+
+               /* Size of the Block Header field */
+               uint32_t size;
+       } block_header;
+
+       /* Information collected when decoding Blocks */
+       struct {
+               /* Observed compressed size of the current Block */
+               vli_type compressed;
+
+               /* Observed uncompressed size of the current Block */
+               vli_type uncompressed;
+
+               /* Number of Blocks decoded so far */
+               vli_type count;
+
+               /*
+                * Hash calculated from the Block sizes. This is used to
+                * validate the Index field.
+                */
+               struct xz_dec_hash hash;
+       } block;
+
+       /* Variables needed when verifying the Index field */
+       struct {
+               /* Position in dec_index() */
+               enum {
+                       SEQ_INDEX_COUNT,
+                       SEQ_INDEX_UNPADDED,
+                       SEQ_INDEX_UNCOMPRESSED
+               } sequence;
+
+               /* Size of the Index in bytes */
+               vli_type size;
+
+               /* Number of Records (matches block.count in valid files) */
+               vli_type count;
+
+               /*
+                * Hash calculated from the Records (matches block.hash in
+                * valid files).
+                */
+               struct xz_dec_hash hash;
+       } index;
+
+       /*
+        * Temporary buffer needed to hold Stream Header, Block Header,
+        * and Stream Footer. The Block Header is the biggest (1 KiB)
+        * so we reserve space according to that. buf[] has to be aligned
+        * to a multiple of four bytes; the size_t variables before it
+        * should guarantee this.
+        */
+       struct {
+               size_t pos;
+               size_t size;
+               uint8_t buf[1024];
+       } temp;
+
+       struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+       struct xz_dec_bcj *bcj;
+       bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+       0,
+       4, 4, 4,
+       8, 8, 8,
+       16, 16, 16,
+       32, 32, 32,
+       64, 64, 64
+};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+       size_t copy_size = min_t(size_t,
+                       b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+       memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+       b->in_pos += copy_size;
+       s->temp.pos += copy_size;
+
+       if (s->temp.pos == s->temp.size) {
+               s->temp.pos = 0;
+               return true;
+       }
+
+       return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
+                          size_t *in_pos, size_t in_size)
+{
+       uint8_t byte;
+
+       if (s->pos == 0)
+               s->vli = 0;
+
+       while (*in_pos < in_size) {
+               byte = in[*in_pos];
+               ++*in_pos;
+
+               s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+               if ((byte & 0x80) == 0) {
+                       /* Don't allow non-minimal encodings. */
+                       if (byte == 0 && s->pos != 0)
+                               return XZ_DATA_ERROR;
+
+                       s->pos = 0;
+                       return XZ_STREAM_END;
+               }
+
+               s->pos += 7;
+               if (s->pos == 7 * VLI_BYTES_MAX)
+                       return XZ_DATA_ERROR;
+       }
+
+       return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 or CRC64 value if presence of
+ * the CRC32 or CRC64 field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       s->in_start = b->in_pos;
+       s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+       if (s->bcj_active)
+               ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+       else
+#endif
+               ret = xz_dec_lzma2_run(s->lzma2, b);
+
+       s->block.compressed += b->in_pos - s->in_start;
+       s->block.uncompressed += b->out_pos - s->out_start;
+
+       /*
+        * There is no need to separately check for VLI_UNKNOWN, since
+        * the observed sizes are always smaller than VLI_UNKNOWN.
+        */
+       if (s->block.compressed > s->block_header.compressed
+                       || s->block.uncompressed
+                               > s->block_header.uncompressed)
+               return XZ_DATA_ERROR;
+
+       if (s->check_type == XZ_CHECK_CRC32)
+               s->crc = xz_crc32(b->out + s->out_start,
+                               b->out_pos - s->out_start, s->crc);
+#ifdef XZ_USE_CRC64
+       else if (s->check_type == XZ_CHECK_CRC64)
+               s->crc = xz_crc64(b->out + s->out_start,
+                               b->out_pos - s->out_start, s->crc);
+#endif
+
+       if (ret == XZ_STREAM_END) {
+               if (s->block_header.compressed != VLI_UNKNOWN
+                               && s->block_header.compressed
+                                       != s->block.compressed)
+                       return XZ_DATA_ERROR;
+
+               if (s->block_header.uncompressed != VLI_UNKNOWN
+                               && s->block_header.uncompressed
+                                       != s->block.uncompressed)
+                       return XZ_DATA_ERROR;
+
+               s->block.hash.unpadded += s->block_header.size
+                               + s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+               s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+               if (s->check_type == XZ_CHECK_CRC32)
+                       s->block.hash.unpadded += 4;
+               else if (IS_CRC64(s->check_type))
+                       s->block.hash.unpadded += 8;
+#endif
+
+               s->block.hash.uncompressed += s->block.uncompressed;
+               s->block.hash.crc32 = xz_crc32(
+                               (const uint8_t *)&s->block.hash,
+                               sizeof(s->block.hash), s->block.hash.crc32);
+
+               ++s->block.count;
+       }
+
+       return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+       size_t in_used = b->in_pos - s->in_start;
+       s->index.size += in_used;
+       s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       do {
+               ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+               if (ret != XZ_STREAM_END) {
+                       index_update(s, b);
+                       return ret;
+               }
+
+               switch (s->index.sequence) {
+               case SEQ_INDEX_COUNT:
+                       s->index.count = s->vli;
+
+                       /*
+                        * Validate that the Number of Records field
+                        * indicates the same number of Records as
+                        * there were Blocks in the Stream.
+                        */
+                       if (s->index.count != s->block.count)
+                               return XZ_DATA_ERROR;
+
+                       s->index.sequence = SEQ_INDEX_UNPADDED;
+                       break;
+
+               case SEQ_INDEX_UNPADDED:
+                       s->index.hash.unpadded += s->vli;
+                       s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+                       break;
+
+               case SEQ_INDEX_UNCOMPRESSED:
+                       s->index.hash.uncompressed += s->vli;
+                       s->index.hash.crc32 = xz_crc32(
+                                       (const uint8_t *)&s->index.hash,
+                                       sizeof(s->index.hash),
+                                       s->index.hash.crc32);
+                       --s->index.count;
+                       s->index.sequence = SEQ_INDEX_UNPADDED;
+                       break;
+               }
+       } while (s->index.count > 0);
+
+       return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four or eight input bytes match the value
+ * of s->crc. s->pos must be zero when starting to validate the first byte.
+ * The "bits" argument allows using the same code for both CRC32 and CRC64.
+ */
+static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
+                               uint32_t bits)
+{
+       do {
+               if (b->in_pos == b->in_size)
+                       return XZ_OK;
+
+               if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
+                       return XZ_DATA_ERROR;
+
+               s->pos += 8;
+
+       } while (s->pos < bits);
+
+       s->crc = 0;
+       s->pos = 0;
+
+       return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+       while (s->pos < check_sizes[s->check_type]) {
+               if (b->in_pos == b->in_size)
+                       return false;
+
+               ++b->in_pos;
+               ++s->pos;
+       }
+
+       s->pos = 0;
+
+       return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+       if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+               return XZ_FORMAT_ERROR;
+
+       if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+                       != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+               return XZ_DATA_ERROR;
+
+       if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+               return XZ_OPTIONS_ERROR;
+
+       /*
+        * Of integrity checks, we support none (Check ID = 0),
+        * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
+        * However, if XZ_DEC_ANY_CHECK is defined, we will accept other
+        * check types too, but then the check won't be verified and
+        * a warning (XZ_UNSUPPORTED_CHECK) will be given.
+        */
+       s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+       if (s->check_type > XZ_CHECK_MAX)
+               return XZ_OPTIONS_ERROR;
+
+       if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+               return XZ_UNSUPPORTED_CHECK;
+#else
+       if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+               return XZ_OPTIONS_ERROR;
+#endif
+
+       return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+       if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+               return XZ_DATA_ERROR;
+
+       if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+               return XZ_DATA_ERROR;
+
+       /*
+        * Validate Backward Size. Note that we never added the size of the
+        * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+        * instead of s->index.size / 4 - 1.
+        */
+       if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+               return XZ_DATA_ERROR;
+
+       if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+               return XZ_DATA_ERROR;
+
+       /*
+        * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+        * for the caller.
+        */
+       return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+       enum xz_ret ret;
+
+       /*
+        * Validate the CRC32. We know that the temp buffer is at least
+        * eight bytes so this is safe.
+        */
+       s->temp.size -= 4;
+       if (xz_crc32(s->temp.buf, s->temp.size, 0)
+                       != get_le32(s->temp.buf + s->temp.size))
+               return XZ_DATA_ERROR;
+
+       s->temp.pos = 2;
+
+       /*
+        * Catch unsupported Block Flags. We support only one or two filters
+        * in the chain, so we catch that with the same test.
+        */
+#ifdef XZ_DEC_BCJ
+       if (s->temp.buf[1] & 0x3E)
+#else
+       if (s->temp.buf[1] & 0x3F)
+#endif
+               return XZ_OPTIONS_ERROR;
+
+       /* Compressed Size */
+       if (s->temp.buf[1] & 0x40) {
+               if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+                                       != XZ_STREAM_END)
+                       return XZ_DATA_ERROR;
+
+               s->block_header.compressed = s->vli;
+       } else {
+               s->block_header.compressed = VLI_UNKNOWN;
+       }
+
+       /* Uncompressed Size */
+       if (s->temp.buf[1] & 0x80) {
+               if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+                               != XZ_STREAM_END)
+                       return XZ_DATA_ERROR;
+
+               s->block_header.uncompressed = s->vli;
+       } else {
+               s->block_header.uncompressed = VLI_UNKNOWN;
+       }
+
+#ifdef XZ_DEC_BCJ
+       /* If there are two filters, the first one must be a BCJ filter. */
+       s->bcj_active = s->temp.buf[1] & 0x01;
+       if (s->bcj_active) {
+               if (s->temp.size - s->temp.pos < 2)
+                       return XZ_OPTIONS_ERROR;
+
+               ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+               if (ret != XZ_OK)
+                       return ret;
+
+               /*
+                * We don't support custom start offset,
+                * so Size of Properties must be zero.
+                */
+               if (s->temp.buf[s->temp.pos++] != 0x00)
+                       return XZ_OPTIONS_ERROR;
+       }
+#endif
+
+       /* Valid Filter Flags always take at least two bytes. */
+       if (s->temp.size - s->temp.pos < 2)
+               return XZ_DATA_ERROR;
+
+       /* Filter ID = LZMA2 */
+       if (s->temp.buf[s->temp.pos++] != 0x21)
+               return XZ_OPTIONS_ERROR;
+
+       /* Size of Properties = 1-byte Filter Properties */
+       if (s->temp.buf[s->temp.pos++] != 0x01)
+               return XZ_OPTIONS_ERROR;
+
+       /* Filter Properties contains LZMA2 dictionary size. */
+       if (s->temp.size - s->temp.pos < 1)
+               return XZ_DATA_ERROR;
+
+       ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+       if (ret != XZ_OK)
+               return ret;
+
+       /* The rest must be Header Padding. */
+       while (s->temp.pos < s->temp.size)
+               if (s->temp.buf[s->temp.pos++] != 0x00)
+                       return XZ_OPTIONS_ERROR;
+
+       s->temp.pos = 0;
+       s->block.compressed = 0;
+       s->block.uncompressed = 0;
+
+       return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       /*
+        * Store the start position for the case when we are in the middle
+        * of the Index field.
+        */
+       s->in_start = b->in_pos;
+
+       while (true) {
+               switch (s->sequence) {
+               case SEQ_STREAM_HEADER:
+                       /*
+                        * Stream Header is copied to s->temp, and then
+                        * decoded from there. This way if the caller
+                        * gives us only little input at a time, we can
+                        * still keep the Stream Header decoding code
+                        * simple. Similar approach is used in many places
+                        * in this file.
+                        */
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       /*
+                        * If dec_stream_header() returns
+                        * XZ_UNSUPPORTED_CHECK, it is still possible
+                        * to continue decoding if working in multi-call
+                        * mode. Thus, update s->sequence before calling
+                        * dec_stream_header().
+                        */
+                       s->sequence = SEQ_BLOCK_START;
+
+                       ret = dec_stream_header(s);
+                       if (ret != XZ_OK)
+                               return ret;
+
+               case SEQ_BLOCK_START:
+                       /* We need one byte of input to continue. */
+                       if (b->in_pos == b->in_size)
+                               return XZ_OK;
+
+                       /* See if this is the beginning of the Index field. */
+                       if (b->in[b->in_pos] == 0) {
+                               s->in_start = b->in_pos++;
+                               s->sequence = SEQ_INDEX;
+                               break;
+                       }
+
+                       /*
+                        * Calculate the size of the Block Header and
+                        * prepare to decode it.
+                        */
+                       s->block_header.size
+                               = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+                       s->temp.size = s->block_header.size;
+                       s->temp.pos = 0;
+                       s->sequence = SEQ_BLOCK_HEADER;
+
+               case SEQ_BLOCK_HEADER:
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       ret = dec_block_header(s);
+                       if (ret != XZ_OK)
+                               return ret;
+
+                       s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+               case SEQ_BLOCK_UNCOMPRESS:
+                       ret = dec_block(s, b);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->sequence = SEQ_BLOCK_PADDING;
+
+               case SEQ_BLOCK_PADDING:
+                       /*
+                        * Size of Compressed Data + Block Padding
+                        * must be a multiple of four. We don't need
+                        * s->block.compressed for anything else
+                        * anymore, so we use it here to test the size
+                        * of the Block Padding field.
+                        */
+                       while (s->block.compressed & 3) {
+                               if (b->in_pos == b->in_size)
+                                       return XZ_OK;
+
+                               if (b->in[b->in_pos++] != 0)
+                                       return XZ_DATA_ERROR;
+
+                               ++s->block.compressed;
+                       }
+
+                       s->sequence = SEQ_BLOCK_CHECK;
+
+               case SEQ_BLOCK_CHECK:
+                       if (s->check_type == XZ_CHECK_CRC32) {
+                               ret = crc_validate(s, b, 32);
+                               if (ret != XZ_STREAM_END)
+                                       return ret;
+                       }
+                       else if (IS_CRC64(s->check_type)) {
+                               ret = crc_validate(s, b, 64);
+                               if (ret != XZ_STREAM_END)
+                                       return ret;
+                       }
+#ifdef XZ_DEC_ANY_CHECK
+                       else if (!check_skip(s, b)) {
+                               return XZ_OK;
+                       }
+#endif
+
+                       s->sequence = SEQ_BLOCK_START;
+                       break;
+
+               case SEQ_INDEX:
+                       ret = dec_index(s, b);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->sequence = SEQ_INDEX_PADDING;
+
+               case SEQ_INDEX_PADDING:
+                       while ((s->index.size + (b->in_pos - s->in_start))
+                                       & 3) {
+                               if (b->in_pos == b->in_size) {
+                                       index_update(s, b);
+                                       return XZ_OK;
+                               }
+
+                               if (b->in[b->in_pos++] != 0)
+                                       return XZ_DATA_ERROR;
+                       }
+
+                       /* Finish the CRC32 value and Index size. */
+                       index_update(s, b);
+
+                       /* Compare the hashes to validate the Index field. */
+                       if (!memeq(&s->block.hash, &s->index.hash,
+                                       sizeof(s->block.hash)))
+                               return XZ_DATA_ERROR;
+
+                       s->sequence = SEQ_INDEX_CRC32;
+
+               case SEQ_INDEX_CRC32:
+                       ret = crc_validate(s, b, 32);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->temp.size = STREAM_HEADER_SIZE;
+                       s->sequence = SEQ_STREAM_FOOTER;
+
+               case SEQ_STREAM_FOOTER:
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       return dec_stream_footer(s);
+               }
+       }
+
+       /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+       size_t in_start;
+       size_t out_start;
+       enum xz_ret ret;
+
+       if (DEC_IS_SINGLE(s->mode))
+               xz_dec_reset(s);
+
+       in_start = b->in_pos;
+       out_start = b->out_pos;
+       ret = dec_main(s, b);
+
+       if (DEC_IS_SINGLE(s->mode)) {
+               if (ret == XZ_OK)
+                       ret = b->in_pos == b->in_size
+                                       ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+               if (ret != XZ_STREAM_END) {
+                       b->in_pos = in_start;
+                       b->out_pos = out_start;
+               }
+
+       } else if (ret == XZ_OK && in_start == b->in_pos
+                       && out_start == b->out_pos) {
+               if (s->allow_buf_error)
+                       ret = XZ_BUF_ERROR;
+
+               s->allow_buf_error = true;
+       } else {
+               s->allow_buf_error = false;
+       }
+
+       return ret;
+}
+
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+       struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s == NULL)
+               return NULL;
+
+       s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+       s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+       if (s->bcj == NULL)
+               goto error_bcj;
+#endif
+
+       s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+       if (s->lzma2 == NULL)
+               goto error_lzma2;
+
+       xz_dec_reset(s);
+       return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+       xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+       kfree(s);
+       return NULL;
+}
+
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+{
+       s->sequence = SEQ_STREAM_HEADER;
+       s->allow_buf_error = false;
+       s->pos = 0;
+       s->crc = 0;
+       memzero(&s->block, sizeof(s->block));
+       memzero(&s->index, sizeof(s->index));
+       s->temp.pos = 0;
+       s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+{
+       if (s != NULL) {
+               xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+               xz_dec_bcj_end(s->bcj);
+#endif
+               kfree(s);
+       }
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_syms.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_syms.c
new file mode 100644 (file)
index 0000000..32eb3c0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * XZ decoder module information
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/module.h>
+#include <linux/xz.h>
+
+EXPORT_SYMBOL(xz_dec_init);
+EXPORT_SYMBOL(xz_dec_reset);
+EXPORT_SYMBOL(xz_dec_run);
+EXPORT_SYMBOL(xz_dec_end);
+
+MODULE_DESCRIPTION("XZ decompressor");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org> and Igor Pavlov");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_test.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_dec_test.c
new file mode 100644 (file)
index 0000000..da28a19
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * XZ decoder tester
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/xz.h>
+
+/* Maximum supported dictionary size */
+#define DICT_MAX (1 << 20)
+
+/* Device name to pass to register_chrdev(). */
+#define DEVICE_NAME "xz_dec_test"
+
+/* Dynamically allocated device major number */
+static int device_major;
+
+/*
+ * We reuse the same decoder state, and thus can decode only one
+ * file at a time.
+ */
+static bool device_is_open;
+
+/* XZ decoder state */
+static struct xz_dec *state;
+
+/*
+ * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
+ * it has returned XZ_STREAM_END, so we make this static.
+ */
+static enum xz_ret ret;
+
+/*
+ * Input and output buffers. The input buffer is used as a temporary safe
+ * place for the data coming from the userspace.
+ */
+static uint8_t buffer_in[1024];
+static uint8_t buffer_out[1024];
+
+/*
+ * Structure to pass the input and output buffers to the XZ decoder.
+ * A few of the fields are never modified so we initialize them here.
+ */
+static struct xz_buf buffers = {
+       .in = buffer_in,
+       .out = buffer_out,
+       .out_size = sizeof(buffer_out)
+};
+
+/*
+ * CRC32 of uncompressed data. This is used to give the user a simple way
+ * to check that the decoder produces correct output.
+ */
+static uint32_t crc;
+
+static int xz_dec_test_open(struct inode *i, struct file *f)
+{
+       if (device_is_open)
+               return -EBUSY;
+
+       device_is_open = true;
+
+       xz_dec_reset(state);
+       ret = XZ_OK;
+       crc = 0xFFFFFFFF;
+
+       buffers.in_pos = 0;
+       buffers.in_size = 0;
+       buffers.out_pos = 0;
+
+       printk(KERN_INFO DEVICE_NAME ": opened\n");
+       return 0;
+}
+
+static int xz_dec_test_release(struct inode *i, struct file *f)
+{
+       device_is_open = false;
+
+       if (ret == XZ_OK)
+               printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
+
+       printk(KERN_INFO DEVICE_NAME ": closed\n");
+       return 0;
+}
+
+/*
+ * Decode the data given to us from the userspace. CRC32 of the uncompressed
+ * data is calculated and is printed at the end of successful decoding. The
+ * uncompressed data isn't stored anywhere for further use.
+ *
+ * The .xz file must have exactly one Stream and no Stream Padding. The data
+ * after the first Stream is considered to be garbage.
+ */
+static ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
+                                size_t size, loff_t *pos)
+{
+       size_t remaining;
+
+       if (ret != XZ_OK) {
+               if (size > 0)
+                       printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
+                                       "garbage at the end of the file\n",
+                                       size);
+
+               return -ENOSPC;
+       }
+
+       printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
+                       size);
+
+       remaining = size;
+       while ((remaining > 0 || buffers.out_pos == buffers.out_size)
+                       && ret == XZ_OK) {
+               if (buffers.in_pos == buffers.in_size) {
+                       buffers.in_pos = 0;
+                       buffers.in_size = min(remaining, sizeof(buffer_in));
+                       if (copy_from_user(buffer_in, buf, buffers.in_size))
+                               return -EFAULT;
+
+                       buf += buffers.in_size;
+                       remaining -= buffers.in_size;
+               }
+
+               buffers.out_pos = 0;
+               ret = xz_dec_run(state, &buffers);
+               crc = crc32(crc, buffer_out, buffers.out_pos);
+       }
+
+       switch (ret) {
+       case XZ_OK:
+               printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
+               return size;
+
+       case XZ_STREAM_END:
+               printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
+                               "CRC32 = 0x%08X\n", ~crc);
+               return size - remaining - (buffers.in_size - buffers.in_pos);
+
+       case XZ_MEMLIMIT_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
+               break;
+
+       case XZ_FORMAT_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
+               break;
+
+       case XZ_OPTIONS_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
+               break;
+
+       case XZ_DATA_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
+               break;
+
+       case XZ_BUF_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
+               break;
+
+       default:
+               printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
+               break;
+       }
+
+       return -EIO;
+}
+
+/* Allocate the XZ decoder state and register the character device. */
+static int __init xz_dec_test_init(void)
+{
+       static const struct file_operations fileops = {
+               .owner = THIS_MODULE,
+               .open = &xz_dec_test_open,
+               .release = &xz_dec_test_release,
+               .write = &xz_dec_test_write
+       };
+
+       state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
+       if (state == NULL)
+               return -ENOMEM;
+
+       device_major = register_chrdev(0, DEVICE_NAME, &fileops);
+       if (device_major < 0) {
+               xz_dec_end(state);
+               return device_major;
+       }
+
+       printk(KERN_INFO DEVICE_NAME ": module loaded\n");
+       printk(KERN_INFO DEVICE_NAME ": Create a device node with "
+                       "'mknod " DEVICE_NAME " c %d 0' and write .xz files "
+                       "to it.\n", device_major);
+       return 0;
+}
+
+static void __exit xz_dec_test_exit(void)
+{
+       unregister_chrdev(device_major, DEVICE_NAME);
+       xz_dec_end(state);
+       printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
+}
+
+module_init(xz_dec_test_init);
+module_exit(xz_dec_test_exit);
+
+MODULE_DESCRIPTION("XZ decompressor tester");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_lzma2.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_lzma2.h
new file mode 100644 (file)
index 0000000..071d67b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ *  - Literal: One 8-bit byte
+ *  - Match: Repeat a chunk of data at some distance
+ *  - Long repeat: Multi-byte match at a recently seen distance
+ *  - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+       STATE_LIT_LIT,
+       STATE_MATCH_LIT_LIT,
+       STATE_REP_LIT_LIT,
+       STATE_SHORTREP_LIT_LIT,
+       STATE_MATCH_LIT,
+       STATE_REP_LIT,
+       STATE_SHORTREP_LIT,
+       STATE_LIT_MATCH,
+       STATE_LIT_LONGREP,
+       STATE_LIT_SHORTREP,
+       STATE_NONLIT_MATCH,
+       STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+       if (*state <= STATE_SHORTREP_LIT_LIT)
+               *state = STATE_LIT_LIT;
+       else if (*state <= STATE_LIT_SHORTREP)
+               *state -= 3;
+       else
+               *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool lzma_state_is_literal(enum lzma_state state)
+{
+       return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ *   - 0x001-0x0FF: Without match byte
+ *   - 0x101-0x1FF: With match byte; match bit is 0
+ *   - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length   Bits
+ *  2-9      4 = Choice=0 + 3 bits
+ * 10-17     5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273   10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+       return len < DIST_STATES + MATCH_LEN_MIN
+                       ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ *   - distance slot: the highest two bits
+ *   - direct bits: 2-26 bits below the highest two bits
+ *   - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_private.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_private.h
new file mode 100644 (file)
index 0000000..482b90f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+#      include <linux/xz.h>
+#      include <linux/kernel.h>
+#      include <asm/unaligned.h>
+       /* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+#      ifndef XZ_PREBOOT
+#              include <linux/slab.h>
+#              include <linux/vmalloc.h>
+#              include <linux/string.h>
+#              ifdef CONFIG_XZ_DEC_X86
+#                      define XZ_DEC_X86
+#              endif
+#              ifdef CONFIG_XZ_DEC_POWERPC
+#                      define XZ_DEC_POWERPC
+#              endif
+#              ifdef CONFIG_XZ_DEC_IA64
+#                      define XZ_DEC_IA64
+#              endif
+#              ifdef CONFIG_XZ_DEC_ARM
+#                      define XZ_DEC_ARM
+#              endif
+#              ifdef CONFIG_XZ_DEC_ARMTHUMB
+#                      define XZ_DEC_ARMTHUMB
+#              endif
+#              ifdef CONFIG_XZ_DEC_SPARC
+#                      define XZ_DEC_SPARC
+#              endif
+#              define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#              define memzero(buf, size) memset(buf, 0, size)
+#      endif
+#      define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+#else
+       /*
+        * For userspace builds, use a separate header to define the required
+        * macros and functions. This makes it easier to adapt the code into
+        * different environments and avoids clutter in the Linux kernel tree.
+        */
+#      include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
+               && !defined(XZ_DEC_DYNALLOC)
+#      define XZ_DEC_SINGLE
+#      define XZ_DEC_PREALLOC
+#      define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+#      define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+#      define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+#      define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+#      define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+#      define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+#      define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+#      define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+#      define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+#      define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+#      if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+                       || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+                       || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+                       || defined(XZ_DEC_SPARC)
+#              define XZ_DEC_BCJ
+#      endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+                                                  uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
+                                        uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+                                      struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+                                    struct xz_dec_lzma2 *lzma2,
+                                    struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_stream.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/lib/xz/xz_stream.h
new file mode 100644 (file)
index 0000000..66cb5a7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+#      include <linux/crc32.h>
+#      undef crc32
+#      define xz_crc32(buf, size, crc) \
+               (~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+       XZ_CHECK_NONE = 0,
+       XZ_CHECK_CRC32 = 1,
+       XZ_CHECK_CRC64 = 4,
+       XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/scripts/xz_wrap.sh b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/linux/scripts/xz_wrap.sh
new file mode 100644 (file)
index 0000000..7a2d372
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This is a wrapper for xz to compress the kernel image using appropriate
+# compression options depending on the architecture.
+#
+# Author: Lasse Collin <lasse.collin@tukaani.org>
+#
+# This file has been put into the public domain.
+# You can do whatever you want with this file.
+#
+
+BCJ=
+LZMA2OPTS=
+
+case $SRCARCH in
+       x86)            BCJ=--x86 ;;
+       powerpc)        BCJ=--powerpc ;;
+       ia64)           BCJ=--ia64; LZMA2OPTS=pb=4 ;;
+       arm)            BCJ=--arm ;;
+       sparc)          BCJ=--sparc ;;
+esac
+
+exec xz --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/boottest.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/boottest.c
new file mode 100644 (file)
index 0000000..1aef5ed
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Test application for xz_boot.c
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define STATIC static
+#define INIT
+
+static void error(/*const*/ char *msg)
+{
+       fprintf(stderr, "%s\n", msg);
+}
+
+/* Disable the CRC64 support even if it was enabled in the Makefile. */
+#undef XZ_USE_CRC64
+
+#include "../linux/lib/decompress_unxz.c"
+
+static uint8_t in[1024 * 1024];
+static uint8_t out[1024 * 1024];
+
+static int fill(void *buf, unsigned int size)
+{
+       return fread(buf, 1, size, stdin);
+}
+
+static int flush(/*const*/ void *buf, unsigned int size)
+{
+       return fwrite(buf, 1, size, stdout);
+}
+
+static void test_buf_to_buf(void)
+{
+       size_t in_size;
+       int ret;
+       in_size = fread(in, 1, sizeof(in), stdin);
+       ret = decompress(in, in_size, NULL, NULL, out, NULL, &error);
+       /* fwrite(out, 1, FIXME, stdout); */
+       fprintf(stderr, "ret = %d\n", ret);
+}
+
+static void test_buf_to_cb(void)
+{
+       size_t in_size;
+       int in_used;
+       int ret;
+       in_size = fread(in, 1, sizeof(in), stdin);
+       ret = decompress(in, in_size, NULL, &flush, NULL, &in_used, &error);
+       fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+static void test_cb_to_cb(void)
+{
+       int ret;
+       ret = decompress(NULL, 0, &fill, &flush, NULL, NULL, &error);
+       fprintf(stderr, "ret = %d\n", ret);
+}
+
+/*
+ * Not used by Linux <= 2.6.37-rc4 and newer probably won't use it either,
+ * but this kind of use case is still required to be supported by the API.
+ */
+static void test_cb_to_buf(void)
+{
+       int in_used;
+       int ret;
+       ret = decompress(in, 0, &fill, NULL, out, &in_used, &error);
+       /* fwrite(out, 1, FIXME, stdout); */
+       fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+int main(int argc, char **argv)
+{
+       if (argc != 2)
+               fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+       else if (strcmp(argv[1], "bb") == 0)
+               test_buf_to_buf();
+       else if (strcmp(argv[1], "bc") == 0)
+               test_buf_to_cb();
+       else if (strcmp(argv[1], "cc") == 0)
+               test_cb_to_cb();
+       else if (strcmp(argv[1], "cb") == 0)
+               test_cb_to_buf();
+       else
+               fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+
+       return 0;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/buftest.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/buftest.c
new file mode 100644 (file)
index 0000000..54b780a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Test application to test buffer-to-buffer decoding
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+#define BUFFER_SIZE (1024 * 1024)
+
+static uint8_t in[BUFFER_SIZE];
+static uint8_t out[BUFFER_SIZE];
+
+int main(void)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+
+       xz_crc32_init();
+
+       s = xz_dec_init(XZ_SINGLE, 0);
+       if (s == NULL) {
+               fputs("Initialization failed", stderr);
+               return 1;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = fread(in, 1, sizeof(in), stdin);
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = sizeof(out);
+
+       ret = xz_dec_run(s, &b);
+       xz_dec_end(s);
+
+       fwrite(out, 1, b.out_pos, stdout);
+       fprintf(stderr, "%d\n", ret);
+
+       return 0;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/bytetest.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/bytetest.c
new file mode 100644 (file)
index 0000000..aa48b9b
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Lazy test for the case when the output size is known
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "xz.h"
+
+static uint8_t in[1];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       const char *msg;
+       size_t uncomp_size;
+
+       if (argc != 2) {
+               fputs("Give uncompressed size as the argument", stderr);
+               return 1;
+       }
+
+       uncomp_size = atoi(argv[1]);
+
+       xz_crc32_init();
+
+       /*
+        * Support up to 64 MiB dictionary. The actually needed memory
+        * is allocated once the headers have been parsed.
+        */
+       s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+       if (s == NULL) {
+               msg = "Memory allocation failed\n";
+               goto error;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = 0;
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = uncomp_size < BUFSIZ ? uncomp_size : BUFSIZ;
+
+       while (true) {
+               if (b.in_pos == b.in_size) {
+                       b.in_size = fread(in, 1, sizeof(in), stdin);
+                       b.in_pos = 0;
+               }
+
+               ret = xz_dec_run(s, &b);
+
+               if (b.out_pos == sizeof(out)) {
+                       if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+                               msg = "Write error\n";
+                               goto error;
+                       }
+
+                       uncomp_size -= b.out_pos;
+                       b.out_pos = 0;
+                       b.out_size = uncomp_size < BUFSIZ
+                                       ? uncomp_size : BUFSIZ;
+               }
+
+               if (ret == XZ_OK)
+                       continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+               if (ret == XZ_UNSUPPORTED_CHECK) {
+                       fputs(argv[0], stderr);
+                       fputs(": ", stderr);
+                       fputs("Unsupported check; not verifying "
+                                       "file integrity\n", stderr);
+                       continue;
+               }
+#endif
+
+               if (uncomp_size != b.out_pos) {
+                       msg = "Uncompressed size doesn't match\n";
+                       goto error;
+               }
+
+               if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+                               || fclose(stdout)) {
+                       msg = "Write error\n";
+                       goto error;
+               }
+
+               switch (ret) {
+               case XZ_STREAM_END:
+                       xz_dec_end(s);
+                       return 0;
+
+               case XZ_MEM_ERROR:
+                       msg = "Memory allocation failed\n";
+                       goto error;
+
+               case XZ_MEMLIMIT_ERROR:
+                       msg = "Memory usage limit reached\n";
+                       goto error;
+
+               case XZ_FORMAT_ERROR:
+                       msg = "Not a .xz file\n";
+                       goto error;
+
+               case XZ_OPTIONS_ERROR:
+                       msg = "Unsupported options in the .xz headers\n";
+                       goto error;
+
+               case XZ_DATA_ERROR:
+               case XZ_BUF_ERROR:
+                       msg = "File is corrupt\n";
+                       goto error;
+
+               default:
+                       msg = "Bug!\n";
+                       goto error;
+               }
+       }
+
+error:
+       xz_dec_end(s);
+       fputs(argv[0], stderr);
+       fputs(": ", stderr);
+       fputs(msg, stderr);
+       return 1;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xz_config.h b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xz_config.h
new file mode 100644 (file)
index 0000000..eb9dac1
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Private includes and definitions for userspace use of XZ Embedded
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_CONFIG_H
+#define XZ_CONFIG_H
+
+/* Uncomment to enable CRC64 support. */
+/* #define XZ_USE_CRC64 */
+
+/* Uncomment as needed to enable BCJ filter decoders. */
+/* #define XZ_DEC_X86 */
+/* #define XZ_DEC_POWERPC */
+/* #define XZ_DEC_IA64 */
+/* #define XZ_DEC_ARM */
+/* #define XZ_DEC_ARMTHUMB */
+/* #define XZ_DEC_SPARC */
+
+/*
+ * MSVC doesn't support modern C but XZ Embedded is mostly C89
+ * so these are enough.
+ */
+#ifdef _MSC_VER
+typedef unsigned char bool;
+#      define true 1
+#      define false 0
+#      define inline __inline
+#else
+#      include <stdbool.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xz.h"
+
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+
+#ifndef min
+#      define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#define min_t(type, x, y) min(x, y)
+
+/*
+ * Some functions have been marked with __always_inline to keep the
+ * performance reasonable even when the compiler is optimizing for
+ * small code size. You may be able to save a few bytes by #defining
+ * __always_inline to plain inline, but don't complain if the code
+ * becomes slow.
+ *
+ * NOTE: System headers on GNU/Linux may #define this macro already,
+ * so if you want to change it, you need to #undef it first.
+ */
+#ifndef __always_inline
+#      ifdef __GNUC__
+#              define __always_inline \
+                       inline __attribute__((__always_inline__))
+#      else
+#              define __always_inline inline
+#      endif
+#endif
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t get_unaligned_le32(const uint8_t *buf)
+{
+       return (uint32_t)buf[0]
+                       | ((uint32_t)buf[1] << 8)
+                       | ((uint32_t)buf[2] << 16)
+                       | ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t get_unaligned_be32(const uint8_t *buf)
+{
+       return (uint32_t)(buf[0] << 24)
+                       | ((uint32_t)buf[1] << 16)
+                       | ((uint32_t)buf[2] << 8)
+                       | (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+       buf[0] = (uint8_t)val;
+       buf[1] = (uint8_t)(val >> 8);
+       buf[2] = (uint8_t)(val >> 16);
+       buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+       buf[0] = (uint8_t)(val >> 24);
+       buf[1] = (uint8_t)(val >> 16);
+       buf[2] = (uint8_t)(val >> 8);
+       buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+#      define get_le32 get_unaligned_le32
+#endif
+
+#endif
diff --git a/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xzminidec.c b/LinuxGUI/Ventoy2Disk/Lib/xz-embedded/userspace/xzminidec.c
new file mode 100644 (file)
index 0000000..ba07413
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Simple XZ decoder command line tool
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is really limited: Not all filters from .xz format are supported,
+ * only CRC32 is supported as the integrity check, and decoding of
+ * concatenated .xz streams is not supported. Thus, you may want to look
+ * at xzdec from XZ Utils if a few KiB bigger tool is not a problem.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+static uint8_t in[BUFSIZ];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       const char *msg;
+
+       if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
+               fputs("Uncompress a .xz file from stdin to stdout.\n"
+                               "Arguments other than `--help' are ignored.\n",
+                               stdout);
+               return 0;
+       }
+
+       xz_crc32_init();
+#ifdef XZ_USE_CRC64
+       xz_crc64_init();
+#endif
+
+       /*
+        * Support up to 64 MiB dictionary. The actually needed memory
+        * is allocated once the headers have been parsed.
+        */
+       s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+       if (s == NULL) {
+               msg = "Memory allocation failed\n";
+               goto error;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = 0;
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = BUFSIZ;
+
+       while (true) {
+               if (b.in_pos == b.in_size) {
+                       b.in_size = fread(in, 1, sizeof(in), stdin);
+                       b.in_pos = 0;
+               }
+
+               ret = xz_dec_run(s, &b);
+
+               if (b.out_pos == sizeof(out)) {
+                       if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+                               msg = "Write error\n";
+                               goto error;
+                       }
+
+                       b.out_pos = 0;
+               }
+
+               if (ret == XZ_OK)
+                       continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+               if (ret == XZ_UNSUPPORTED_CHECK) {
+                       fputs(argv[0], stderr);
+                       fputs(": ", stderr);
+                       fputs("Unsupported check; not verifying "
+                                       "file integrity\n", stderr);
+                       continue;
+               }
+#endif
+
+               if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+                               || fclose(stdout)) {
+                       msg = "Write error\n";
+                       goto error;
+               }
+
+               switch (ret) {
+               case XZ_STREAM_END:
+                       xz_dec_end(s);
+                       return 0;
+
+               case XZ_MEM_ERROR:
+                       msg = "Memory allocation failed\n";
+                       goto error;
+
+               case XZ_MEMLIMIT_ERROR:
+                       msg = "Memory usage limit reached\n";
+                       goto error;
+
+               case XZ_FORMAT_ERROR:
+                       msg = "Not a .xz file\n";
+                       goto error;
+
+               case XZ_OPTIONS_ERROR:
+                       msg = "Unsupported options in the .xz headers\n";
+                       goto error;
+
+               case XZ_DATA_ERROR:
+               case XZ_BUF_ERROR:
+                       msg = "File is corrupt\n";
+                       goto error;
+
+               default:
+                       msg = "Bug!\n";
+                       goto error;
+               }
+       }
+
+error:
+       xz_dec_end(s);
+       fputs(argv[0], stderr);
+       fputs(": ", stderr);
+       fputs(msg, stderr);
+       return 1;
+}
diff --git a/LinuxGUI/Ventoy2Disk/Web/ventoy_http.c b/LinuxGUI/Ventoy2Disk/Web/ventoy_http.c
new file mode 100644 (file)
index 0000000..755a08c
--- /dev/null
@@ -0,0 +1,1481 @@
+/******************************************************************************
+ * ventoy_http.c  ---- ventoy http
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <ventoy_define.h>
+#include <ventoy_json.h>
+#include <ventoy_util.h>
+#include <ventoy_disk.h>
+#include <ventoy_http.h>
+#include "fat_filelib.h"
+
+static pthread_mutex_t g_api_mutex;
+static char g_cur_language[128];
+static int  g_cur_part_style = 0;
+static char g_cur_server_token[64];
+static struct mg_context *g_ventoy_http_ctx = NULL;
+
+static uint32_t g_efi_part_offset = 0;
+static uint8_t *g_efi_part_raw_img = NULL;
+static uint8_t *g_grub_stg1_raw_img = NULL;
+
+static char g_cur_process_diskname[64];
+static char g_cur_process_type[64];
+static int g_cur_process_result = 0;
+static PROGRESS_POINT g_current_progress = PT_FINISH;
+
+static int ventoy_load_mbr_template(void)
+{
+    FILE *fp = NULL;
+
+    fp = fopen("boot/boot.img", "rb");
+    if (fp == NULL)
+    {
+        vlog("Failed to open file boot/boot.img\n");
+        return 1;
+    }
+
+    fread(g_mbr_template, 1, 512, fp);
+    fclose(fp);
+    
+    ventoy_gen_preudo_uuid(g_mbr_template + 0x180);    
+    return 0;
+}
+
+static int ventoy_disk_xz_flush(void *src, unsigned int size)
+{
+    memcpy(g_efi_part_raw_img + g_efi_part_offset, src, size);
+    g_efi_part_offset += size;
+
+    g_current_progress = PT_LOAD_DISK_IMG + (g_efi_part_offset / SIZE_1MB);
+    return (int)size;
+}
+
+static int ventoy_unxz_efipart_img(void)
+{
+    int rc;
+    int inlen;
+    int xzlen;
+    void *xzbuf = NULL;
+    uint8_t *buf = NULL;
+
+    rc = ventoy_read_file_to_buf(VENTOY_FILE_DISK_IMG, 0, &xzbuf, &xzlen);
+    vdebug("read disk.img.xz rc:%d len:%d\n", rc, xzlen);
+
+    if (g_efi_part_raw_img)
+    {
+        buf = g_efi_part_raw_img;
+    }
+    else
+    {
+        buf = malloc(VTOYEFI_PART_BYTES);
+        if (!buf)
+        {
+            check_free(xzbuf);
+            return 1;
+        }
+    }
+
+    g_efi_part_offset = 0;
+    g_efi_part_raw_img = buf;
+    
+    rc = unxz(xzbuf, xzlen, NULL, ventoy_disk_xz_flush, buf, &inlen, NULL);
+    vdebug("ventoy_unxz_efipart_img len:%d rc:%d unxzlen:%u\n", inlen, rc, g_efi_part_offset);
+
+    check_free(xzbuf);
+    return 0;
+}
+
+static int ventoy_unxz_stg1_img(void)
+{
+    int rc;
+    int inlen;
+    int xzlen;
+    void *xzbuf = NULL;
+    uint8_t *buf = NULL;
+
+    rc = ventoy_read_file_to_buf(VENTOY_FILE_STG1_IMG, 0, &xzbuf, &xzlen);
+    vdebug("read core.img.xz rc:%d len:%d\n", rc, xzlen);
+
+    if (g_grub_stg1_raw_img)
+    {
+        buf = g_grub_stg1_raw_img;
+    }
+    else
+    {
+        buf = zalloc(SIZE_1MB);
+        if (!buf)
+        {
+            check_free(xzbuf);
+            return 1;
+        }
+    }
+    
+    rc = unxz(xzbuf, xzlen, NULL, NULL, buf, &inlen, NULL);
+    vdebug("ventoy_unxz_stg1_img len:%d rc:%d\n", inlen, rc);
+
+    g_grub_stg1_raw_img = buf;
+
+    check_free(xzbuf);
+    return 0;
+}
+
+
+static int ventoy_http_save_cfg(void)
+{
+    FILE *fp;
+
+    fp = fopen("./Ventoy2Disk.ini", "w");
+    if (!fp)
+    {
+        return 0;
+    }
+
+    fprintf(fp, "[Ventoy]\nLanguage=%s\nPartStyle=%d\n", g_cur_language, g_cur_part_style);
+
+    fclose(fp);
+    return 0;
+}
+
+static int ventoy_http_load_cfg(void)
+{
+    int i;
+    int len;
+    char line[256];
+    FILE *fp;
+
+    fp = fopen("./Ventoy2Disk.ini", "r");
+    if (!fp)
+    {
+        return 0;
+    }
+
+    while (fgets(line, sizeof(line), fp))
+    {
+        len = (int)strlen(line);
+        for (i = len - 1; i >= 0; i--)
+        {
+            if (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n')
+            {
+                line[i] = 0;
+            }
+            else
+            {
+                break;
+            }
+        }
+    
+        len = (int)strlen("Language=");
+        if (strncmp(line, "Language=", len) == 0)
+        {
+            scnprintf(g_cur_language, "%s", line + len);
+        }
+        else if (strncmp(line, "PartStyle=", strlen("PartStyle=")) == 0)
+        {
+            g_cur_part_style = (int)strtol(line + strlen("PartStyle="), NULL, 10);
+        }
+    }
+
+    fclose(fp);
+    return 0;
+}
+
+
+static int ventoy_json_result(struct mg_connection *conn, const char *err)
+{
+    mg_printf(conn, 
+              "HTTP/1.1 200 OK \r\n"
+              "Content-Type: application/json\r\n"
+              "Content-Length: %d\r\n"
+              "\r\n%s",
+              (int)strlen(err), err);
+    return 0;
+}
+
+static int ventoy_json_buffer(struct mg_connection *conn, const char *json_buf, int json_len)
+{
+    mg_printf(conn, 
+              "HTTP/1.1 200 OK \r\n"
+              "Content-Type: application/json\r\n"
+              "Content-Length: %d\r\n"
+              "\r\n%s",
+              json_len, json_buf);
+    return 0;
+}
+
+static int ventoy_api_sysinfo(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int busy = 0;
+    int pos = 0;
+    int buflen = 0;
+    char buf[512];
+    
+    (void)json;
+
+    busy = (g_current_progress == PT_FINISH) ? 0 : 1;
+
+    buflen = sizeof(buf) - 1;
+    VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+    VTOY_JSON_FMT_OBJ_BEGIN();
+    VTOY_JSON_FMT_STRN("token", g_cur_server_token);
+    VTOY_JSON_FMT_STRN("language", g_cur_language);
+    VTOY_JSON_FMT_STRN("ventoy_ver", ventoy_get_local_version());
+    VTOY_JSON_FMT_UINT("partstyle", g_cur_part_style);
+    VTOY_JSON_FMT_BOOL("busy", busy);
+    VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname);
+    VTOY_JSON_FMT_STRN("process_type", g_cur_process_type);
+    VTOY_JSON_FMT_OBJ_END();
+    VTOY_JSON_FMT_END(pos);
+
+    ventoy_json_buffer(conn, buf, pos);
+    return 0;
+}
+
+static int ventoy_api_get_percent(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int pos = 0;
+    int buflen = 0;
+    int percent = 0;
+    char buf[128];
+    
+    (void)json;
+
+    percent = g_current_progress * 100 / PT_FINISH;
+
+    buflen = sizeof(buf) - 1;
+    VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+    VTOY_JSON_FMT_OBJ_BEGIN();
+    VTOY_JSON_FMT_STRN("result", g_cur_process_result ? "failed" : "success");
+    VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname);
+    VTOY_JSON_FMT_STRN("process_type", g_cur_process_type);
+    VTOY_JSON_FMT_UINT("percent", percent);
+    VTOY_JSON_FMT_OBJ_END();
+    VTOY_JSON_FMT_END(pos);
+
+    ventoy_json_buffer(conn, buf, pos);
+    return 0;
+}
+
+static int ventoy_api_set_language(struct mg_connection *conn, VTOY_JSON *json)
+{
+    const char *lang = NULL;
+    
+    lang = vtoy_json_get_string_ex(json, "language");
+    if (lang)
+    {
+        scnprintf(g_cur_language, "%s", lang);
+        ventoy_http_save_cfg();
+    }
+
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;    
+}
+
+static int ventoy_api_set_partstyle(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int ret;
+    int style = 0;
+    
+    ret = vtoy_json_get_int(json, "partstyle", &style);
+    if (JSON_SUCCESS == ret)
+    {
+        if ((style == 0) || (style == 1))
+        {
+            g_cur_part_style = style;
+            ventoy_http_save_cfg();            
+        }
+    }
+
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;    
+}
+
+static int ventoy_clean_disk(int fd, uint64_t size)
+{
+    int zerolen;
+    ssize_t len;
+    off_t offset;
+    void *buf = NULL;
+    
+    vdebug("ventoy_clean_disk fd:%d size:%llu\n", fd, (_ull)size);
+
+    zerolen = 64 * 1024;
+    buf = zalloc(zerolen);
+    if (!buf)
+    {
+        vlog("failed to alloc clean buffer\n");
+        return 1;
+    }
+
+    offset = lseek(fd, 0, SEEK_SET);
+    len = write(fd, buf, zerolen);
+    vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen);
+
+    offset = lseek(fd, size - zerolen, SEEK_SET);
+    len = write(fd, buf, zerolen);
+    vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen);
+
+    free(buf);
+    return 0;
+}
+
+static int ventoy_write_legacy_grub(int fd, int partstyle)
+{
+    ssize_t len;
+    off_t offset;
+    
+    if (partstyle)
+    {
+        vlog("Write GPT stage1 ...\n");
+
+        offset = lseek(fd, 512 * 34, SEEK_SET);
+
+        g_grub_stg1_raw_img[500] = 35;//update blocklist
+        len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512 * 34);
+
+        vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512 * 34, (_ull)len, SIZE_1MB - 512 * 34);
+        if (SIZE_1MB - 512 * 34 != len)
+        {
+            vlog("write length error\n");
+            return 1;
+        }
+    }
+    else
+    {
+        vlog("Write MBR stage1 ...\n");
+        offset = lseek(fd, 512, SEEK_SET);
+        len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512);
+        
+        vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512, (_ull)len, SIZE_1MB - 512);
+        if (SIZE_1MB - 512 != len)
+        {
+            vlog("write length error\n");
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int VentoyFatMemRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+       uint32 i;
+       uint32 offset;
+
+       for (i = 0; i < SectorCount; i++)
+       {
+               offset = (Sector + i) * 512;
+        memcpy(Buffer + i * 512, g_efi_part_raw_img + offset, 512);
+       }
+
+       return 1;
+}
+
+static int VentoyFatMemWrite(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+       uint32 i;
+       uint32 offset;
+
+       for (i = 0; i < SectorCount; i++)
+       {
+               offset = (Sector + i) * 512;
+        memcpy(g_efi_part_raw_img + offset, Buffer + i * 512, 512);
+       }
+
+       return 1;
+}
+
+static int VentoyProcSecureBoot(int SecureBoot)
+{
+       int rc = 0;
+       int size;
+       char *filebuf = NULL;
+       void *file = NULL;
+
+       vlog("VentoyProcSecureBoot %d ...\n", SecureBoot);
+       
+       if (SecureBoot)
+       {
+               vlog("Secure boot is enabled ...\n");
+               return 0;
+       }
+
+       fl_init();
+
+       if (0 == fl_attach_media(VentoyFatMemRead, VentoyFatMemWrite))
+       {
+               file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+               vlog("Open ventoy efi file %p \n", file);
+               if (file)
+               {
+                       fl_fseek(file, 0, SEEK_END);
+                       size = (int)fl_ftell(file);
+                       fl_fseek(file, 0, SEEK_SET);
+
+                       vlog("ventoy efi file size %d ...\n", size);
+
+                       filebuf = (char *)malloc(size);
+                       if (filebuf)
+                       {
+                               fl_fread(filebuf, 1, size, file);
+                       }
+
+                       fl_fclose(file);
+
+                       vlog("Now delete all efi files ...\n");
+                       fl_remove("/EFI/BOOT/BOOTX64.EFI");
+                       fl_remove("/EFI/BOOT/grubx64.efi");
+                       fl_remove("/EFI/BOOT/grubx64_real.efi");
+                       fl_remove("/EFI/BOOT/MokManager.efi");
+            fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer");
+
+                       file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb");
+                       vlog("Open bootx64 efi file %p \n", file);
+                       if (file)
+                       {
+                               if (filebuf)
+                               {
+                                       fl_fwrite(filebuf, 1, size, file);
+                               }
+                               
+                               fl_fflush(file);
+                               fl_fclose(file);
+                       }
+
+                       if (filebuf)
+                       {
+                               free(filebuf);
+                       }
+               }
+
+        file = fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb");
+        vlog("Open ventoy efi file %p\n", file);
+        if (file)
+        {
+            fl_fseek(file, 0, SEEK_END);
+            size = (int)fl_ftell(file);
+            fl_fseek(file, 0, SEEK_SET);
+
+            vlog("ventoy efi file size %d ...\n", size);
+
+            filebuf = (char *)malloc(size);
+            if (filebuf)
+            {
+                fl_fread(filebuf, 1, size, file);
+            }
+
+            fl_fclose(file);
+
+            vlog("Now delete all efi files ...\n");
+            fl_remove("/EFI/BOOT/BOOTIA32.EFI");
+            fl_remove("/EFI/BOOT/grubia32.efi");
+            fl_remove("/EFI/BOOT/grubia32_real.efi");
+            fl_remove("/EFI/BOOT/mmia32.efi");            
+
+            file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
+            vlog("Open bootia32 efi file %p\n", file);
+            if (file)
+            {
+                if (filebuf)
+                {
+                    fl_fwrite(filebuf, 1, size, file);
+                }
+
+                fl_fflush(file);
+                fl_fclose(file);
+            }
+
+            if (filebuf)
+            {
+                free(filebuf);
+            }
+        }
+
+       }
+       else
+       {
+               rc = 1;
+       }
+
+       fl_shutdown();
+
+       return rc;
+}
+
+static int ventoy_check_efi_part_data(int fd, uint64_t offset)
+{
+    int i;
+    ssize_t len;
+    char *buf;
+
+    buf = malloc(SIZE_1MB);
+    if (!buf)
+    {
+        return 0;
+    }
+    
+    lseek(fd, offset, SEEK_SET);
+    for (i = 0; i < 32; i++)
+    {
+        len = read(fd, buf, SIZE_1MB);
+        if (len != SIZE_1MB || memcmp(buf, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB))
+        {
+            vlog("part2 data check failed i=%d len:%llu\n", i, (_ull)len);
+            return 1;
+        }
+
+        g_current_progress = PT_CHECK_PART2 + (i / 4);
+    }
+
+    return 0;
+}
+
+static int ventoy_write_efipart(int fd, uint64_t offset, uint32_t secureboot)
+{
+    int i;
+    ssize_t len;
+    
+    vlog("Formatting part2 EFI offset:%llu ...\n", (_ull)offset);
+    lseek(fd, offset, SEEK_SET);
+
+    VentoyProcSecureBoot((int)secureboot);
+
+    g_current_progress = PT_WRITE_VENTOY_START;
+    for (i = 0; i < 32; i++)
+    {
+        len = write(fd, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB);
+        vlog("write disk writelen:%lld datalen:%d [ %s ]\n", 
+            (_ll)len, SIZE_1MB, (len == SIZE_1MB) ? "success" : "failed");
+        
+        if (len != SIZE_1MB)
+        {
+            vlog("failed to format part2 EFI\n");
+            return 1;
+        }
+    
+        g_current_progress = PT_WRITE_VENTOY_START + i / 4;
+    }
+
+    return 0;
+}
+
+static int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
+{
+    uint64_t LBA;
+    uint64_t BackupLBA;
+
+    memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
+
+    LBA = pHead->EfiStartLBA;
+    BackupLBA = pHead->EfiBackupLBA;
+    
+    pHead->EfiStartLBA = BackupLBA;
+    pHead->EfiBackupLBA = LBA;
+    pHead->PartTblStartLBA = BackupLBA + 1 - 33;
+
+    pHead->Crc = 0;
+    pHead->Crc = ventoy_crc32(pHead, pHead->Length);
+
+    return 0;
+}
+
+static int ventoy_write_gpt_part_table(int fd, uint64_t disksize, VTOY_GPT_INFO *gpt)
+{
+    ssize_t len;
+    off_t offset;
+    VTOY_GPT_HDR BackupHead;
+    
+    VentoyFillBackupGptHead(gpt, &BackupHead);
+
+    offset = lseek(fd, disksize - 512, SEEK_SET);
+    len = write(fd, &BackupHead, sizeof(VTOY_GPT_HDR));
+    vlog("write backup gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+    if (offset != disksize - 512 || len != sizeof(VTOY_GPT_HDR))
+    {
+        return 1;
+    }
+
+    offset = lseek(fd, disksize - 512 * 33, SEEK_SET);
+    len = write(fd, gpt->PartTbl, sizeof(gpt->PartTbl));
+    vlog("write main gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+    if (offset != disksize - 512 * 33 || len != sizeof(gpt->PartTbl))
+    {
+        return 1;
+    }
+    
+    offset = lseek(fd, 0, SEEK_SET);
+    len = write(fd, gpt, sizeof(VTOY_GPT_INFO));
+    vlog("write gpt part head off:%llu len:%llu\n", (_ull)offset, (_ull)len);
+    if (offset != 0 || len != sizeof(VTOY_GPT_INFO))
+    {
+        return 1;
+    }
+    
+    return 0;
+}
+
+static void * ventoy_update_thread(void *data)
+{
+    int fd;
+    ssize_t len;
+    off_t offset;
+    MBR_HEAD MBR;
+    ventoy_disk *disk = NULL;
+    ventoy_thread_data *thread = (ventoy_thread_data *)data;
+
+    vdebug("ventoy_update_thread run ...\n");
+
+    fd = thread->diskfd;
+    disk = thread->disk;
+
+    g_current_progress = PT_PRAPARE_FOR_CLEAN;
+    vdebug("check disk %s\n", disk->disk_name);
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("disk is mounted, now try to unmount it ...\n");
+        ventoy_try_umount_disk(disk->disk_path);
+    }
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("%s is mounted and can't umount!\n", disk->disk_path);
+        goto err;
+    }
+    else
+    {
+        vlog("disk is not mounted now, we can do continue ...\n");
+    }
+
+    g_current_progress = PT_LOAD_CORE_IMG;
+    ventoy_unxz_stg1_img();
+    
+    g_current_progress = PT_LOAD_DISK_IMG;
+    ventoy_unxz_efipart_img();
+
+    g_current_progress = PT_FORMAT_PART2;
+
+    vlog("Formatting part2 EFI ...\n");
+    if (0 != ventoy_write_efipart(fd, disk->vtoydata.part2_start_sector * 512, thread->secure_boot))
+    {
+        vlog("Failed to format part2 efi ...\n");
+        goto err;
+    }
+
+    g_current_progress = PT_WRITE_STG1_IMG;
+
+    vlog("Writing legacy grub ...\n");
+    if (0 != ventoy_write_legacy_grub(fd, disk->vtoydata.partition_style))
+    {
+        vlog("ventoy_write_legacy_grub failed ...\n");
+        goto err;
+    }
+
+    offset = lseek(fd, 512 * 2040, SEEK_SET);
+    len = write(fd, disk->vtoydata.rsvdata, sizeof(disk->vtoydata.rsvdata));
+    vlog("Writing reserve data offset:%llu len:%llu ...\n", (_ull)offset, (_ull)len);
+
+    memcpy(&MBR, &(disk->vtoydata.gptinfo.MBR), 512);
+    if (disk->vtoydata.partition_style == 0 && MBR.PartTbl[0].Active == 0)
+    {
+        MBR.PartTbl[0].Active = 0x80;
+        MBR.PartTbl[1].Active = 0;
+        MBR.PartTbl[2].Active = 0;
+        MBR.PartTbl[3].Active = 0;
+    
+        offset = lseek(fd, 0, SEEK_SET);
+        len = write(fd, &MBR, 512);
+        vlog("set MBR partition 1 active flag enabled offset:%llu len:%llu\n", (_ull)offset, (_ull)len);
+    }
+    
+    g_current_progress = PT_SYNC_DATA1;
+
+    vlog("fsync data1...\n");
+    fsync(fd);
+    vtoy_safe_close_fd(fd);
+
+    g_current_progress = PT_SYNC_DATA2;
+
+    vlog("====================================\n");
+    vlog("====== ventoy update success ======\n");
+    vlog("====================================\n");
+    goto end;
+
+err:
+    g_cur_process_result = 1;
+    vtoy_safe_close_fd(fd);        
+
+end:
+    g_current_progress = PT_FINISH;
+
+    check_free(thread);
+    
+    return NULL;
+}
+
+static void * ventoy_install_thread(void *data)
+{
+    int fd;
+    ssize_t len;
+    off_t offset;
+    MBR_HEAD MBR;
+    ventoy_disk *disk = NULL;
+    VTOY_GPT_INFO *gpt = NULL;
+    ventoy_thread_data *thread = (ventoy_thread_data *)data;
+    uint64_t Part1StartSector = 0;
+    uint64_t Part1SectorCount = 0;
+    uint64_t Part2StartSector = 0;
+
+    vdebug("ventoy_install_thread run ...\n");
+
+    fd = thread->diskfd;
+    disk = thread->disk;
+
+    g_current_progress = PT_PRAPARE_FOR_CLEAN;
+    vdebug("check disk %s\n", disk->disk_name);
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("disk is mounted, now try to unmount it ...\n");
+        ventoy_try_umount_disk(disk->disk_path);
+    }
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("%s is mounted and can't umount!\n", disk->disk_path);
+        goto err;
+    }
+    else
+    {
+        vlog("disk is not mounted now, we can do continue ...\n");
+    }
+
+    g_current_progress = PT_DEL_ALL_PART;
+    ventoy_clean_disk(fd, disk->size_in_byte);
+    
+    g_current_progress = PT_LOAD_CORE_IMG;
+    ventoy_unxz_stg1_img();
+    
+    g_current_progress = PT_LOAD_DISK_IMG;
+    ventoy_unxz_efipart_img();
+
+    if (thread->partstyle)
+    {
+        vdebug("Fill GPT part table\n");
+        gpt = zalloc(sizeof(VTOY_GPT_INFO));
+        ventoy_fill_gpt(disk->size_in_byte, thread->reserveBytes, thread->align4kb, gpt);
+        Part1StartSector = gpt->PartTbl[0].StartLBA;
+        Part1SectorCount = gpt->PartTbl[0].LastLBA - Part1StartSector + 1;
+        Part2StartSector = gpt->PartTbl[1].StartLBA;
+    }
+    else
+    {
+        vdebug("Fill MBR part table\n");
+        ventoy_fill_mbr(disk->size_in_byte, thread->reserveBytes, thread->align4kb, 0, &MBR);
+        Part1StartSector = MBR.PartTbl[0].StartSectorId;
+        Part1SectorCount = MBR.PartTbl[0].SectorCount;
+        Part2StartSector = MBR.PartTbl[1].StartSectorId;
+    }
+
+    vlog("Part1StartSector:%llu Part1SectorCount:%llu Part2StartSector:%llu\n", 
+        (_ull)Part1StartSector, (_ull)Part1SectorCount, (_ull)Part2StartSector);
+
+    if (thread->partstyle != disk->partstyle)
+    {
+        vlog("Wait for format part1 (partstyle changed) ...\n");
+        sleep(1);
+    }
+
+    g_current_progress = PT_FORMAT_PART1;
+    vlog("Formatting part1 exFAT %s ...\n", disk->disk_path);
+    if (0 != mkexfat_main(disk->disk_path, fd, Part1SectorCount))
+    {
+        vlog("Failed to format exfat ...\n");
+        goto err;
+    }
+
+    g_current_progress = PT_FORMAT_PART2;
+    vlog("Formatting part2 EFI ...\n");
+    if (0 != ventoy_write_efipart(fd, Part2StartSector * 512, thread->secure_boot))
+    {
+        vlog("Failed to format part2 efi ...\n");
+        goto err;
+    }
+
+    g_current_progress = PT_WRITE_STG1_IMG;
+    vlog("Writing legacy grub ...\n");
+    if (0 != ventoy_write_legacy_grub(fd, thread->partstyle))
+    {
+        vlog("ventoy_write_legacy_grub failed ...\n");
+        goto err;
+    }
+
+    g_current_progress = PT_SYNC_DATA1;
+    vlog("fsync data1...\n");
+    fsync(fd);
+    vtoy_safe_close_fd(fd);
+
+    /* reopen for check part2 data */
+    vlog("Checking part2 efi data %s ...\n", disk->disk_path);
+    g_current_progress = PT_CHECK_PART2;
+    fd = open(disk->disk_path, O_RDONLY | O_BINARY);
+    if (fd < 0)
+    {
+        vlog("failed to open %s for check fd:%d err:%d\n", disk->disk_path, fd, errno);
+        goto err;
+    }
+
+    if (0 == ventoy_check_efi_part_data(fd, Part2StartSector * 512))
+    {
+        vlog("efi part data check success\n");
+    }
+    else
+    {
+        vlog("efi part data check failed\n");
+        goto err;
+    }
+
+    vtoy_safe_close_fd(fd);
+    
+    /* reopen for write part table */
+    g_current_progress = PT_WRITE_PART_TABLE;
+    vlog("Writting Partition Table style:%d...\n", thread->partstyle);
+
+    fd = open(disk->disk_path, O_RDWR | O_BINARY);
+    if (fd < 0)
+    {
+        vlog("failed to open %s for part table fd:%d err:%d\n", disk->disk_path, fd, errno);
+        goto err;
+    }
+
+    if (thread->partstyle)
+    {
+        ventoy_write_gpt_part_table(fd, disk->size_in_byte, gpt);
+    }
+    else
+    {
+        offset = lseek(fd, 0, SEEK_SET);
+        len = write(fd, &MBR, 512);
+        vlog("Writting MBR Partition Table %llu %llu\n", (_ull)offset, (_ull)len);
+        if (offset != 0 || len != 512)
+        {
+            goto err;
+        }
+    }
+
+    g_current_progress = PT_SYNC_DATA2;
+    vlog("fsync data2...\n");
+    fsync(fd);
+    vtoy_safe_close_fd(fd);
+
+
+    vlog("====================================\n");
+    vlog("====== ventoy install success ======\n");
+    vlog("====================================\n");
+    goto end;
+
+err:
+    g_cur_process_result = 1;
+    vtoy_safe_close_fd(fd);        
+
+end:
+    g_current_progress = PT_FINISH;
+
+    check_free(gpt);
+    check_free(thread);
+    
+    return NULL;
+}
+
+static int ventoy_api_clean(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int i = 0;
+    int fd = 0;
+    ventoy_disk *disk = NULL;
+    const char *diskname = NULL;
+    char path[128];
+    
+    if (g_current_progress != PT_FINISH)
+    {
+        ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+        return 0;  
+    }
+    
+    diskname = vtoy_json_get_string_ex(json, "disk");
+    if (diskname == NULL)
+    {
+        ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+        return 0;
+    }
+
+    for (i = 0; i < g_disk_num; i++)
+    {
+        if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+        {
+            disk = g_disk_list + i;
+            break;
+        }
+    }
+
+    if (disk == NULL)
+    {
+        vlog("disk %s not found\n", diskname);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    scnprintf(path, "/sys/block/%s", diskname);
+    if (access(path, F_OK) < 0)
+    {
+        vlog("File %s not exist anymore\n", path);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    vlog("==================================\n");
+    vlog("===== ventoy clean %s =====\n", disk->disk_path);
+    vlog("==================================\n");
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("disk is mounted, now try to unmount it ...\n");
+        ventoy_try_umount_disk(disk->disk_path);
+    }
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("%s is mounted and can't umount!\n", disk->disk_path);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+    else
+    {
+        vlog("disk is not mounted now, we can do the clean ...\n");
+    }
+
+    fd = open(disk->disk_path, O_RDWR | O_BINARY);
+    if (fd < 0)
+    {
+        vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+
+    vdebug("start clean %s ...\n", disk->disk_model);
+    ventoy_clean_disk(fd, disk->size_in_byte);    
+
+    vtoy_safe_close_fd(fd);
+    
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;    
+}
+
+static int ventoy_api_install(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int i = 0;
+    int ret = 0;
+    int fd = 0;
+    uint32_t align4kb = 0;
+    uint32_t style = 0;
+    uint32_t secure_boot = 0;
+    uint64_t reserveBytes = 0;
+    ventoy_disk *disk = NULL;
+    const char *diskname = NULL;
+    const char *reserve_space = NULL;
+    ventoy_thread_data *thread = NULL;
+    char path[128];
+    
+    if (g_current_progress != PT_FINISH)
+    {
+        ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+        return 0;  
+    }
+    
+    diskname = vtoy_json_get_string_ex(json, "disk");
+    reserve_space = vtoy_json_get_string_ex(json, "reserve_space");
+    ret += vtoy_json_get_uint(json, "partstyle", &style);
+    ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot);
+    ret += vtoy_json_get_uint(json, "align_4kb", &align4kb);
+
+    if (diskname == NULL || reserve_space == NULL || ret != JSON_SUCCESS)
+    {
+        ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+        return 0;
+    }
+
+    reserveBytes = (uint64_t)strtoull(reserve_space, NULL, 10);
+
+    for (i = 0; i < g_disk_num; i++)
+    {
+        if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+        {
+            disk = g_disk_list + i;
+            break;
+        }
+    }
+
+    if (disk == NULL)
+    {
+        vlog("disk %s not found\n", diskname);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    scnprintf(path, "/sys/block/%s", diskname);
+    if (access(path, F_OK) < 0)
+    {
+        vlog("File %s not exist anymore\n", path);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    if (disk->size_in_byte > 2199023255552ULL && style == 0)
+    {
+        vlog("disk %s is more than 2TB and GPT is needed\n", path);
+        ventoy_json_result(conn, VTOY_JSON_MBR_2TB_RET);
+        return 0;
+    }
+
+    if ((reserveBytes + VTOYEFI_PART_BYTES * 2) > disk->size_in_byte)
+    {
+        vlog("reserve space %llu is too big for disk %s %llu\n", (_ull)reserveBytes, path, (_ull)disk->size_in_byte);
+        ventoy_json_result(conn, VTOY_JSON_INVALID_RSV_RET);
+        return 0;
+    }
+
+    vlog("==================================================================================\n");
+    vlog("===== ventoy install %s style:%s secureboot:%u align4K:%u reserve:%llu =========\n",
+         disk->disk_path, (style ? "GPT" : "MBR"), secure_boot, align4kb, (_ull)reserveBytes);
+    vlog("==================================================================================\n");
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("disk is mounted, now try to unmount it ...\n");
+        ventoy_try_umount_disk(disk->disk_path);
+    }
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("%s is mounted and can't umount!\n", disk->disk_path);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+    else
+    {
+        vlog("disk is not mounted now, we can do the install ...\n");
+    }
+
+    fd = open(disk->disk_path, O_RDWR | O_BINARY);
+    if (fd < 0)
+    {
+        vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+
+    vdebug("start install thread %s ...\n", disk->disk_model);
+    thread = zalloc(sizeof(ventoy_thread_data));
+    if (!thread)
+    {
+        vtoy_safe_close_fd(fd);
+        vlog("failed to alloc thread data err:%d\n", errno);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+    
+    g_current_progress = PT_START;
+    g_cur_process_result = 0;
+    scnprintf(g_cur_process_type, "%s", "install");
+    scnprintf(g_cur_process_diskname, "%s", disk->disk_name);
+
+    thread->disk = disk;
+    thread->diskfd = fd;
+    thread->align4kb = align4kb;
+    thread->partstyle = style;
+    thread->secure_boot = secure_boot;
+    thread->reserveBytes = reserveBytes;
+    
+    mg_start_thread(ventoy_install_thread, thread);
+    
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;    
+}
+
+static int ventoy_api_update(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int i = 0;
+    int ret = 0;
+    int fd = 0;
+    uint32_t secure_boot = 0;
+    ventoy_disk *disk = NULL;
+    const char *diskname = NULL;
+    ventoy_thread_data *thread = NULL;
+    char path[128];
+    
+    if (g_current_progress != PT_FINISH)
+    {
+        ventoy_json_result(conn, VTOY_JSON_BUSY_RET);
+        return 0;  
+    }
+    
+    diskname = vtoy_json_get_string_ex(json, "disk");
+    ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot);
+    if (diskname == NULL || ret != JSON_SUCCESS)
+    {
+        ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+        return 0;
+    }
+
+    for (i = 0; i < g_disk_num; i++)
+    {
+        if (strcmp(g_disk_list[i].disk_name, diskname) == 0)
+        {
+            disk = g_disk_list + i;
+            break;
+        }
+    }
+
+    if (disk == NULL)
+    {
+        vlog("disk %s not found\n", diskname);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    if (disk->vtoydata.ventoy_valid == 0)
+    {
+        vlog("disk %s is not ventoy disk\n", diskname);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+
+    scnprintf(path, "/sys/block/%s", diskname);
+    if (access(path, F_OK) < 0)
+    {
+        vlog("File %s not exist anymore\n", path);
+        ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET);
+        return 0;
+    }
+
+    vlog("==========================================================\n");
+    vlog("===== ventoy update %s new_secureboot:%u =========\n", disk->disk_path, secure_boot);
+    vlog("==========================================================\n");
+
+    vlog("%s version:%s partstyle:%u oldsecureboot:%u reserve:%llu\n", 
+        disk->disk_path, disk->vtoydata.ventoy_ver, 
+        disk->vtoydata.partition_style,
+        disk->vtoydata.secure_boot_flag,
+        (_ull)(disk->vtoydata.preserved_space)
+        );
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("disk is mounted, now try to unmount it ...\n");
+        ventoy_try_umount_disk(disk->disk_path);
+    }
+
+    if (ventoy_is_disk_mounted(disk->disk_path))
+    {
+        vlog("%s is mounted and can't umount!\n", disk->disk_path);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+    else
+    {
+        vlog("disk is not mounted now, we can do the update ...\n");
+    }
+
+    fd = open(disk->disk_path, O_RDWR | O_BINARY);
+    if (fd < 0)
+    {
+        vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+
+    vdebug("start update thread %s ...\n", disk->disk_model);
+    thread = zalloc(sizeof(ventoy_thread_data));
+    if (!thread)
+    {
+        vtoy_safe_close_fd(fd);
+        vlog("failed to alloc thread data err:%d\n", errno);
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+    
+    g_current_progress = PT_START;
+    g_cur_process_result = 0;
+    scnprintf(g_cur_process_type, "%s", "update");
+    scnprintf(g_cur_process_diskname, "%s", disk->disk_name);
+
+    thread->disk = disk;
+    thread->diskfd = fd;
+    thread->secure_boot = secure_boot;
+    
+    mg_start_thread(ventoy_update_thread, thread);
+    
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;    
+}
+
+
+static int ventoy_api_refresh_device(struct mg_connection *conn, VTOY_JSON *json)
+{
+    (void)json;
+
+    if (g_current_progress == PT_FINISH)
+    {
+        g_disk_num = 0;
+        ventoy_disk_enumerate_all();
+    }
+
+    ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+    return 0;
+}
+
+static int ventoy_api_get_dev_list(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int i = 0;
+    int rc = 0;
+    int pos = 0;
+    int buflen = 0;
+    uint32_t alldev = 0;
+    char *buf = NULL;
+    ventoy_disk *cur = NULL;
+    
+    rc = vtoy_json_get_uint(json, "alldev", &alldev);
+    if (JSON_SUCCESS != rc)
+    {
+        alldev = 0;
+    }
+
+    buflen = g_disk_num * 1024;
+    buf = (char *)malloc(buflen + 1024);
+    if (!buf)
+    {
+        ventoy_json_result(conn, VTOY_JSON_FAILED_RET);
+        return 0;
+    }
+
+    VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
+    VTOY_JSON_FMT_OBJ_BEGIN();
+    VTOY_JSON_FMT_KEY("list");
+    VTOY_JSON_FMT_ARY_BEGIN();
+
+    for (i = 0; i < g_disk_num; i++)
+    {
+        cur = g_disk_list + i;
+
+        if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
+        {
+            continue;
+        }
+        
+        VTOY_JSON_FMT_OBJ_BEGIN();
+        VTOY_JSON_FMT_STRN("name", cur->disk_name);
+        VTOY_JSON_FMT_STRN("model", cur->disk_model);
+        VTOY_JSON_FMT_STRN("size", cur->human_readable_size);
+        VTOY_JSON_FMT_UINT("vtoy_valid", cur->vtoydata.ventoy_valid);
+        VTOY_JSON_FMT_STRN("vtoy_ver", cur->vtoydata.ventoy_ver);
+        VTOY_JSON_FMT_UINT("vtoy_secure_boot", cur->vtoydata.secure_boot_flag);
+        VTOY_JSON_FMT_UINT("vtoy_partstyle", cur->vtoydata.partition_style);
+        VTOY_JSON_FMT_OBJ_ENDEX();
+    }
+    
+    VTOY_JSON_FMT_ARY_END();
+    VTOY_JSON_FMT_OBJ_END();
+    VTOY_JSON_FMT_END(pos);
+
+    ventoy_json_buffer(conn, buf, pos);
+    return 0;
+}
+
+static JSON_CB g_ventoy_json_cb[] = 
+{
+    { "sysinfo",        ventoy_api_sysinfo        },
+    { "sel_language",   ventoy_api_set_language   },
+    { "sel_partstyle",  ventoy_api_set_partstyle  },
+    { "refresh_device", ventoy_api_refresh_device },
+    { "get_dev_list",   ventoy_api_get_dev_list   },
+    { "install",        ventoy_api_install        },
+    { "update",         ventoy_api_update         },
+    { "clean",          ventoy_api_clean          },
+    { "get_percent",    ventoy_api_get_percent    },
+};
+
+static int ventoy_json_handler(struct mg_connection *conn, VTOY_JSON *json)
+{
+    int i;
+    const char *token = NULL;
+    const char *method = NULL;
+
+    method = vtoy_json_get_string_ex(json, "method");
+    if (!method)
+    {
+        ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET);
+        return 0;
+    }
+
+    if (strcmp(method, "sysinfo"))
+    {
+        token = vtoy_json_get_string_ex(json, "token");
+        if (token == NULL || strcmp(token, g_cur_server_token))
+        {
+            ventoy_json_result(conn, VTOY_JSON_TOKEN_ERR_RET);
+            return 0;
+        }
+    }
+
+    for (i = 0; i < (int)(sizeof(g_ventoy_json_cb) / sizeof(g_ventoy_json_cb[0])); i++)
+    {
+        if (strcmp(method, g_ventoy_json_cb[i].method) == 0)
+        {
+            g_ventoy_json_cb[i].callback(conn, json);
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int ventoy_request_handler(struct mg_connection *conn)
+{
+    int post_data_len;
+    int post_buf_len;
+    VTOY_JSON *json = NULL;
+    char *post_data_buf = NULL;
+    const struct mg_request_info *ri = NULL;
+    char stack_buf[512];
+    
+    ri = mg_get_request_info(conn);    
+
+    if (strcmp(ri->uri, "/vtoy/json") == 0)
+    {
+        if (ri->content_length > 500)
+        {
+            post_data_buf = malloc(ri->content_length + 4);
+            post_buf_len  = ri->content_length + 1;
+        }
+        else
+        {
+            post_data_buf = stack_buf;
+            post_buf_len = sizeof(stack_buf);
+        }
+        
+        post_data_len = mg_read(conn, post_data_buf, post_buf_len);
+        post_data_buf[post_data_len] = 0;
+
+        json = vtoy_json_create();
+        if (JSON_SUCCESS == vtoy_json_parse(json, post_data_buf))
+        {
+            pthread_mutex_lock(&g_api_mutex);
+            ventoy_json_handler(conn, json->pstChild);
+            pthread_mutex_unlock(&g_api_mutex);
+        }
+        else
+        {
+            ventoy_json_result(conn, VTOY_JSON_INVALID_RET);
+        }
+
+        vtoy_json_destroy(json);
+
+        if (post_data_buf != stack_buf)
+        {
+            free(post_data_buf);
+        }
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+int ventoy_http_start(const char *ip, const char *port)
+{
+    uint8_t uuid[16];
+    char addr[128];
+    struct mg_callbacks callbacks;
+    const char *options[] = 
+    {
+           "listening_ports",    "24680",
+        "document_root",      "WebUI",
+        "error_log_file",     VTOY_LOG_FILE,
+           "request_timeout_ms", "10000",
+            NULL
+    };
+
+    /* unique token */
+    ventoy_gen_preudo_uuid(uuid);
+    scnprintf(g_cur_server_token, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+        uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+        uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+
+    /* option */
+    scnprintf(addr, "%s:%s", ip, port);
+    options[1] = addr;
+
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.begin_request = ventoy_request_handler;
+    g_ventoy_http_ctx = mg_start(&callbacks, NULL, options);
+
+    return g_ventoy_http_ctx ? 0 : 1;
+}
+
+int ventoy_http_stop(void)
+{
+    if (g_ventoy_http_ctx)
+    {
+        mg_stop(g_ventoy_http_ctx);        
+    }
+    return 0;
+}
+
+int ventoy_http_init(void)
+{
+    pthread_mutex_init(&g_api_mutex, NULL);
+
+    ventoy_http_load_cfg();
+
+    ventoy_load_mbr_template();
+    
+    return 0;
+}
+
+void ventoy_http_exit(void)
+{
+    pthread_mutex_destroy(&g_api_mutex);
+
+    check_free(g_efi_part_raw_img);
+    g_efi_part_raw_img = NULL;    
+}
+
diff --git a/LinuxGUI/Ventoy2Disk/Web/ventoy_http.h b/LinuxGUI/Ventoy2Disk/Web/ventoy_http.h
new file mode 100644 (file)
index 0000000..8c12c02
--- /dev/null
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * ventoy_http.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_HTTP_H__
+#define __VENTOY_HTTP_H__
+
+#include <civetweb.h>
+
+typedef enum PROGRESS_POINT
+{
+    PT_START = 1,
+
+    PT_PRAPARE_FOR_CLEAN,
+    PT_DEL_ALL_PART,
+
+    PT_LOAD_CORE_IMG,
+    PT_LOAD_DISK_IMG,
+    PT_UNXZ_DISK_IMG_FINISH = PT_LOAD_DISK_IMG + 32,
+
+    PT_FORMAT_PART1, //10
+    
+    PT_FORMAT_PART2,
+
+    PT_WRITE_VENTOY_START,
+    PT_WRITE_VENTOY_FINISH = PT_WRITE_VENTOY_START + 8,
+
+    PT_WRITE_STG1_IMG,//45
+    PT_SYNC_DATA1,
+    
+    PT_CHECK_PART2,
+    PT_CHECK_PART2_FINISH = PT_CHECK_PART2 + 8,
+
+    PT_WRITE_PART_TABLE,//52
+    PT_SYNC_DATA2,
+    
+    PT_FINISH
+}PROGRESS_POINT;
+
+typedef int (*ventoy_json_callback)(struct mg_connection *conn, VTOY_JSON *json);
+typedef struct JSON_CB
+{
+    const char *method;
+    ventoy_json_callback callback;
+}JSON_CB;
+
+typedef struct ventoy_thread_data
+{
+    int diskfd;
+    uint32_t align4kb;
+    uint32_t partstyle;
+    uint32_t secure_boot;
+    uint64_t reserveBytes;
+    ventoy_disk *disk;
+}ventoy_thread_data;
+
+extern int g_vtoy_exfat_disk_fd;
+extern uint64_t g_vtoy_exfat_part_size;
+
+int ventoy_http_init(void);
+void ventoy_http_exit(void);
+int ventoy_http_start(const char *ip, const char *port);
+int ventoy_http_stop(void);
+int mkexfat_main(const char *devpath, int fd, uint64_t part_sector_count);
+
+#endif /* __VENTOY_HTTP_H__ */
+
diff --git a/LinuxGUI/Ventoy2Disk/main.c b/LinuxGUI/Ventoy2Disk/main.c
new file mode 100644 (file)
index 0000000..efdc170
--- /dev/null
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <ventoy_define.h>
+#include <ventoy_util.h>
+#include <ventoy_json.h>
+#include <ventoy_disk.h>
+#include <ventoy_http.h>
+
+int ventoy_log_init(void);
+void ventoy_log_exit(void);
+
+void ventoy_signal_stop(int sig)
+{
+    vlog("ventoy server exit due to signal ...\n");
+    printf("ventoy server exit ...\n");
+    
+    ventoy_http_stop();
+    ventoy_http_exit();
+    ventoy_disk_exit();
+    ventoy_log_exit();
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int rc;
+    const char *ip = "127.0.0.1";
+    const char *port = "24680";
+
+    if (argc != 3)
+    {
+        printf("Invalid argc %d\n", argc);
+        return 1;
+    }
+
+    if (isdigit(argv[1][0]))
+    {
+        ip = argv[1];
+    }
+    
+    if (isdigit(argv[2][0]))
+    {
+        port = argv[2];
+    }
+
+    ventoy_log_init();
+
+    vlog("===============================================\n");
+    vlog("===== Ventoy2Disk %s %s:%s =====\n", ventoy_get_local_version(), ip, port);
+    vlog("===============================================\n");
+
+    ventoy_disk_init();
+
+    ventoy_http_init();
+    
+    rc = ventoy_http_start(ip, port);
+    if (rc)
+    {
+        printf("failed to start http server\n");
+    }
+    else
+    {
+        signal(SIGINT, ventoy_signal_stop);
+        signal(SIGTSTP, ventoy_signal_stop);
+        signal(SIGQUIT, ventoy_signal_stop);
+        while (1)
+        {
+            sleep(100);
+        }
+    }
+    
+    return 0;
+}
+
diff --git a/LinuxGUI/WebUI/favicon.ico b/LinuxGUI/WebUI/favicon.ico
new file mode 100644 (file)
index 0000000..9c4273b
Binary files /dev/null and b/LinuxGUI/WebUI/favicon.ico differ
diff --git a/LinuxGUI/WebUI/index.html b/LinuxGUI/WebUI/index.html
new file mode 100644 (file)
index 0000000..5c91c97
--- /dev/null
@@ -0,0 +1,1062 @@
+<html>\r
+<head>\r
+    <meta charset="utf-8">\r
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">\r
+    <!-- HTTP 1.1 -->  \r
+    <meta http-equiv="pragma" content="no-cache">  \r
+    <!-- HTTP 1.0 -->  \r
+    <meta http-equiv="cache-control" content="no-cache">  \r
+    <title>Ventoy2Disk</title>\r
+    <!-- Tell the browser to be responsive to screen width -->\r
+    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">\r
+    <!-- Bootstrap 3.3.5 -->\r
+    <link rel="stylesheet" href="static/bootstrap/css/bootstrap.min.css">\r
+    <!-- Font Awesome -->\r
+    <link rel="stylesheet" href="static/css/font-awesome.min.css">\r
+    <!-- Ionicons -->\r
+    <link rel="stylesheet" href="static/css/ionicons.min.css">\r
+    <!-- Theme style -->\r
+    <link rel="stylesheet" href="static/AdminLTE/css/AdminLTE.min.css">\r
+    <!-- AdminLTE Skins. Choose a skin from the css/skins\r
+         folder instead of downloading all of them to reduce the load. -->    \r
+    \r
+    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->\r
+    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\r
+    <!--[if lt IE 9]>\r
+        <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>\r
+        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>\r
+    <![endif]-->\r
+    \r
+    <!-- ./wrapper -->\r
+    <!-- jQuery 2.1.4 -->\r
+    <script src="static/js/jQuery-2.1.4.min.js"></script>\r
+    <!-- jquery validate -->\r
+    <script src="static/js/jquery.validate.min.js"></script>\r
+    <!-- Bootstrap 3.3.5 -->\r
+    <script src="static/bootstrap/js/bootstrap.min.js"></script>\r
+    <!-- AdminLTE App -->\r
+    <script src="static/AdminLTE/js/app.min.js"></script>    \r
+    <script src="static/js/jquery.vtoy.alert.js"></script>\r
+    <script src="static/js/vtoy.js"></script>    \r
+    <script src="static/js/languages.js"></script>        \r
+    \r
+    \r
+    <style type="text/css">\r
+    * {\r
+         font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;\r
+     }\r
+    .modal {\r
+        padding-top: 80px;\r
+    }\r
+    .treeview-menu a {\r
+        margin-left: 20px;\r
+    }\r
+    span.vtoy_ver {\r
+        display:inline-block;\r
+        font-size:28px;\r
+        font-weight:bold;\r
+        color:red; \r
+        width:60%; \r
+        text-align:center;\r
+    }\r
+    span.vtoy_part {\r
+        display:inline-block;\r
+        font-size:14px;\r
+        font-weight:bold;\r
+        width:15%; \r
+        text-align:right;\r
+    }\r
+    span.vtoy_secure {\r
+        display:inline-block;\r
+        font-size:20px;\r
+        font-weight:bold;\r
+        width:15%; \r
+        color:#ea991f;\r
+        text-align:center;\r
+        visibility: hidden;\r
+    }\r
+    .noselect {\r
+      -webkit-touch-callout: none; /* iOS Safari */\r
+        -webkit-user-select: none; /* Safari */\r
+         -khtml-user-select: none; /* Konqueror HTML */\r
+           -moz-user-select: none; /* Old versions of Firefox */\r
+            -ms-user-select: none; /* Internet Explorer/Edge */\r
+                user-select: none; /* Non-prefixed version, currently\r
+                                      supported by Chrome, Opera and Firefox */\r
+    }\r
+    span.select {\r
+        visibility:hidden;\r
+        color:green;\r
+    }\r
+    \r
+    .dropdown-submenu {\r
+      position: relative;\r
+    }\r
+\r
+    .dropdown-submenu>.dropdown-menu {\r
+      top: 0;\r
+      left: 100%;\r
+      margin-top: -6px;\r
+      margin-left: -1px;\r
+      -webkit-border-radius: 0 6px 6px 6px;\r
+      -moz-border-radius: 0 6px 6px;\r
+      border-radius: 0 6px 6px 6px;\r
+    }\r
+\r
+    .dropdown-submenu:hover>.dropdown-menu {\r
+      display: block;\r
+    }\r
+\r
+    .dropdown-submenu>a:after {\r
+      display: block;\r
+      content: " ";\r
+      float: right;\r
+      width: 0;\r
+      height: 0;\r
+      border-color: transparent;\r
+      border-style: solid;\r
+      border-width: 5px 0 5px 5px;\r
+      border-left-color: #ccc;\r
+      margin-top: 5px;\r
+      margin-right: -10px;\r
+    }\r
+\r
+    .dropdown-submenu:hover>a:after {\r
+      border-left-color: #fff;\r
+    }\r
+\r
+    .dropdown-submenu.pull-left {\r
+      float: none;\r
+    }\r
+\r
+    .dropdown-submenu.pull-left>.dropdown-menu {\r
+      left: -100%;\r
+      margin-left: 10px;\r
+      -webkit-border-radius: 6px 0 6px 6px;\r
+      -moz-border-radius: 6px 0 6px 6px;\r
+      border-radius: 6px 0 6px 6px;\r
+    }\r
+        \r
+    select.vtoyselect {\r
+        -moz-appearance: none;\r
+        -webkit-appearance: none;\r
+        appearance: none;\r
+        background-image: url("static/img/dropdown.png");\r
+        background-repeat: no-repeat;\r
+        background-position: calc(100% - 7px) 50%;\r
+        background-size: 2% auto;\r
+        border-radius:3px;\r
+        padding:0;\r
+        padding-left:15px;\r
+    }\r
+    \r
+    select.vtoyselect2 {\r
+        -moz-appearance: none;\r
+        -webkit-appearance: none;\r
+        appearance: none;\r
+        background-image: url("static/img/dropdown.png");\r
+        background-repeat: no-repeat;\r
+        background-position: calc(100% - 7px) 50%;\r
+        background-size: 10% auto;\r
+        border-radius:3px;\r
+        padding:0;\r
+        padding-left:15px;\r
+    }\r
+    \r
+    select:-moz-focusring {\r
+        color: transparent;\r
+        text-shadow: 0 0 0 #555;\r
+    }\r
+    \r
+    </style>\r
+</head>\r
+\r
+<body style="overflow:hidden">\r
+    <div class="wrapper" >\r
+        <!-- Content Wrapper. Contains page content -->\r
+        <div id='vtoy_main_div' style='width:540px; position:absolute;'>\r
+        \r
+<div class="modal" id="vtoy_set_part_cfg_modal" >\r
+    <div class="modal-dialog" id="vtoy_set_part_cfg_modal_dlg" style="position:absolute;">\r
+        <div class="modal-content">\r
+            <form id="vtoy_set_part_cfg_form" class="form-horizontal">\r
+                <div class="modal-header">\r
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">\r
+                        <span aria-hidden="true">&times;</span>\r
+                    </button>\r
+                    <h4 id='part_config_dlg_title'>Configuration</h4>\r
+                </div>\r
+                <div class="modal-body">\r
+                        <div class="form-group">\r
+                            <div class="col-sm-10 checkbox">\r
+                                <label >\r
+                                    <input type="checkbox" id="vtoy_preserve_space_checkbox" onclick="on_enable_preserve_space()"/>\r
+                                    <span id="vtoy_preserve_space_tip" style="font-size:14px;font-weight:bold;">在磁盘最后保留一段空间</span>\r
+                                </label>\r
+                            </div>                                \r
+                        </div>\r
+                \r
+                        <div class="form-group" style="width:520px;">\r
+                            <div class="col-sm-6" style="float:left; width:350px;">\r
+                                <input type="number" min="1" class="form-control" id="vtoy_preserve_space" style="font-family: couriew new;font-size: 14px;"/>                                \r
+                            </div>\r
+                            <div class="col-sm-6" style="float:left; width:150px;">\r
+                                <select id="vtoy_space_unit_dropbox" class="form-control valid vtoyselect2" aria-invalid="false">\r
+                                    <option value="0" selected="selected">GB</option>\r
+                                    <option value="1">MB</option>\r
+                                </select>\r
+                            </div>\r
+                        </div>\r
+                        \r
+                        <hr/>\r
+                        \r
+                        <div class="form-group">\r
+                            <div class="col-sm-10 checkbox">\r
+                                <label >\r
+                                    <input type="checkbox" id="vtoy_part_align_4kb"/>\r
+                                    <span id="vtoy_part_4kb_align_tip" style="font-size:14px;font-weight:bold;">分区按照 4KB 对齐</span>\r
+                                </label>\r
+                            </div>                                \r
+                        </div>\r
+                        \r
+                </div>\r
+                <div class="modal-footer">\r
+                    <button type="button" id='vtoy_modal_btn_ok'     class="btn btn-primary btn-flat">确定</button>                        \r
+                    <button type="button" id='vtoy_modal_btn_cancel' class="btn btn-default btn-flat">取消</button>\r
+                </div>\r
+            </form>\r
+        </div>\r
+    </div>\r
+</div>\r
+\r
+        \r
+<!-- Main content -->\r
+<section class="content" id="vtoy-content">\r
+<div >\r
+    <div class="row" style="margin-top:-10px; width:535px; font-family:courier new; font-weight:bold;align:center;text-align:center;">\r
+        <ul class="nav nav-tabs" style="margin-left:5px;">\r
+          <li class="dropdown">\r
+            <a class="dropdown-toggle noselect" id='vtoy_option' data-toggle="dropdown" aria-expanded="false" style="cursor: pointer;">配置选项 <span class="caret"></span></a>\r
+            <ul class="dropdown-menu noselect">\r
+              <li role="presentation"><a role="menuitem" onclick="on_secure_boot()"><span id='vtoy_check_secure_boot' class="fa fa-check select"></span><span  id='vtoy_menu_secure_boot'>安全启动支持</span><span class="fa fa-check select"></span></a></li>              \r
+\r
+              <li class="dropdown-submenu">\r
+                <a id='vtoy_menu_secure_boot'><span class="fa fa-check select"></span><span id='vtoy_menu_part_style'>分区类型</span><span class="fa fa-check select"></span></a>\r
+                <ul class="dropdown-menu">\r
+                    <li><a onclick="on_select_mbr_click()"><span id='vtoy_mbr_check' class="fa fa-check select"></span> MBR</a></li>\r
+                    <li><a onclick="on_select_gpt_click()"><span id='vtoy_gpt_check' class="fa fa-check select"></span> GPT</a></li>\r
+                </ul>\r
+              </li>              \r
+              <li role="presentation"><a role="menuitem" onclick="on_config_partition()"><span class="fa fa-check select"></span><span id='vtoy_menu_part_config'>分区设置</span><span class="fa fa-check select"></span></a></li>              \r
+              <li role="presentation"><a role="menuitem" onclick="on_clean_ventoy()"><span class="fa fa-check select"></span><span id='vtoy_menu_clean_ventoy'>清除 Ventoy</span><span class="fa fa-check select"></span></a></li>              \r
+              <li role="presentation"><a role="menuitem" onclick="on_show_all_device()"><span id='vtoy_check_show_all_dev' class="fa fa-check select"></span><span id='vtoy_menu_show_all_device'>显示所有设备</span><span class="fa fa-check select"></span></a></li>              \r
+            </ul>\r
+          </li>\r
+          <li class="dropdown">\r
+            <a class="dropdown-toggle noselect" id='vtoy_language' data-toggle="dropdown" aria-expanded="false" style="cursor: pointer;;">Languages <span class="caret"></span></a>\r
+            <ul class="dropdown-menu pull-left noselect" style="height:300px;overflow:scroll"  id='vtoy_language_dropbox'>              \r
+            </ul>\r
+          </li>\r
+        </ul>\r
+    </div>\r
+\r
+    <div class="box-body" style=" font-weight:bold; " >      \r
+        <div class="row" style="width:520px;">\r
+            <div style="float:left; width:480px;">\r
+                <span id="vtoy_dev_title" style="font-weight:bold; margin-left:2px;">设备</span>\r
+            </div>\r
+            \r
+            <div style="float:left; width:480px; margin-top:2px;" >\r
+                <select id="vtoy_dev_list" class="form-control valid vtoyselect" aria-invalid="false">                \r
+                </select>\r
+            </div>\r
+          \r
+            <div style="float:right;">\r
+                <img src="static/img/refresh.ico" alt="" id="refresh_dev_img" style="cursor: pointer;"></img>\r
+            </div>\r
+        </div>\r
+          \r
+        <div class="row" style="width:520px;margin-top:20px;">          \r
+              <div class="box box-primary box-solid" style="float:left; width:250px;">\r
+                <div class="box-header with-border" style="text-align:center;">\r
+                  <h3 class="box-title" id="vtoy_local_ver_title" style="font-size: 14px;font-weight: bold;" >安装包内 Ventoy 版本</h3>              \r
+                </div>\r
+                <div class="box-body no-padding" style="height:40px;">\r
+                <span class="vtoy_secure fa fa-lock" id="vtoy_select_secure_icon"></span>\r
+                    <span class="vtoy_ver" id="vtoy_local_ver"></span>\r
+                    <span class="vtoy_part" id="vtoy_local_part_style"></span>\r
+                </div>\r
+              </div>\r
+              \r
+              <div class="box box-primary box-solid" style="float:right; width:250px;">\r
+                <div class="box-header with-border" style="text-align:center;">\r
+                  <h3 class="box-title" id="vtoy_dev_ver_title" style="font-size: 14px;font-weight: bold;" >设备内部 Ventoy 版本</h3>              \r
+                </div>\r
+                <div class="box-body no-padding" style="height:40px;">\r
+                    <span class="vtoy_secure fa fa-lock" id="vtoy_dev_secure_icon"></span>\r
+                    <span class="vtoy_ver" id="vtoy_dev_ver"></span>\r
+                    <span class="vtoy_part" id="vtoy_dev_part_style"></span>\r
+                </div>\r
+              </div>\r
+        </div>\r
+          \r
+        <div class="row"  style="width:520px;">\r
+            <span id="vtoy_status_title" style="font-weight:bold; margin-left:2px;">状态 - 准备就绪</span>\r
+            <div class="progress">\r
+                <div id='vtoy_progress_bar' class="progress-bar progress-bar-success progress-bar-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">                  \r
+                </div>\r
+            </div>\r
+        </div>\r
+        \r
+        <div class="row"  style="width:520px;margin-top:2px;">\r
+            <button id="VtoyBtnInstall" onclick="on_vtoy_install()" class="btn btn-default" style="font-weight:bold; width:150px;margin-left:70px;">安装</button>\r
+            <button id="VtoyBtnUpdate" onclick="on_vtoy_update()" class="btn btn-default" style="font-weight:bold; width:150px;margin-left:70px;">升级</button>\r
+        </div>\r
+    </div>    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+</div>\r
+            </section>\r
+            <!-- /.content -->\r
+        </div>\r
+        \r
+        <!-- /.content-wrapper -->\r
+    </div>\r
+    \r
+<script type="text/javascript">\r
+    \r
+    var vtoy_in_progress = false;\r
+    var vtoy_app_width = 550;\r
+    var vtoy_app_height = 400;\r
+    var vtoy_cur_language;\r
+    var vtoy_cur_lang_index = -1;\r
+    var vtoy_client_language;\r
+    var vtoy_selected_part_style = 0;\r
+    var vtoy_current_token = 'xx';\r
+    var vtoy_cur_dev_list;\r
+    var vtoy_cur_process_disk_name;\r
+    var vtoy_chrome_app_mode = (window.location.href.indexOf('chrome-app') >= 0) ? 1 : 0;\r
+\r
+    function sort_language_list() {\r
+        var tmp;\r
+        for (var i = 0; i < vtoy_language_data.length; i++) {\r
+            for (var j = i + 1; j < vtoy_language_data.length; j++) {\r
+                if (vtoy_language_data[i].name === 'Chinese Simplified (简体中文)') {\r
+                    break;\r
+                }\r
+                else if (vtoy_language_data[j].name === 'Chinese Simplified (简体中文)' || vtoy_language_data[i].name > vtoy_language_data[j].name) {\r
+                    tmp = vtoy_language_data[i];\r
+                    vtoy_language_data[i] = vtoy_language_data[j];\r
+                    vtoy_language_data[j] = tmp;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    \r
+    function fill_language_list() {\r
+        var html;\r
+        var dropbox = $('ul#vtoy_language_dropbox');        \r
+        for (var i = 0; i < vtoy_language_data.length; i++) {\r
+            html = '<li role="presentation"><a  onclick="on_language_click(' + i + ')"  role="menuitem" tabindex="' + i + '">' + \r
+            '<span id="vtoy_check_' + i + '" class="fa fa-check" style="visibility:hidden;color:green;" ></span> ' + \r
+            vtoy_language_data[i].name + '</a></li>';\r
+            dropbox.append(html);\r
+        }\r
+    }\r
+\r
+    function update_language(lastIndex, newIndex) {\r
+        if (lastIndex >= 0) {\r
+            $('span#vtoy_check_' + lastIndex).css("visibility","hidden");\r
+        }        \r
+        $('span#vtoy_check_' + newIndex).css("visibility","visible");\r
+        \r
+        vtoy_cur_lang_index = newIndex;\r
+        \r
+        $('a#vtoy_option').html(vtoy_cur_language.STR_MENU_OPTION + ' <span class="caret"></span>');\r
+        $('h3#vtoy_local_ver_title').text(vtoy_cur_language.STR_LOCAL_VER);\r
+        $('h3#vtoy_dev_ver_title').text(vtoy_cur_language.STR_DISK_VER);\r
+        $('span#vtoy_status_title').text(vtoy_cur_language.STR_STATUS);\r
+        $('span#vtoy_dev_title').text(vtoy_cur_language.STR_DEVICE);\r
+        \r
+        $('span#vtoy_menu_secure_boot').text(vtoy_cur_language.STR_MENU_SECURE_BOOT);\r
+        $('span#vtoy_menu_part_style').text(vtoy_cur_language.STR_MENU_PART_STYLE);\r
+        $('span#vtoy_menu_part_config').text(vtoy_cur_language.STR_MENU_PART_CFG);\r
+        $('span#vtoy_menu_clean_ventoy').text(vtoy_cur_language.STR_MENU_CLEAR);\r
+        $('span#vtoy_menu_show_all_device').text(vtoy_cur_language.STR_SHOW_ALL_DEV);\r
+        \r
+        $('span#vtoy_preserve_space_tip').text(vtoy_cur_language.STR_PRESERVE_SPACE);        \r
+        $('span#vtoy_part_4kb_align_tip').text(vtoy_cur_language.STR_PART_ALIGN_4KB);        \r
+        \r
+        $('button#VtoyBtnInstall').text(vtoy_cur_language.STR_INSTALL);\r
+        $('button#VtoyBtnUpdate').text(vtoy_cur_language.STR_UPDATE);\r
+        \r
+        $('button#vtoy_modal_btn_ok').text(vtoy_cur_language.STR_BTN_OK);\r
+        $('button#vtoy_modal_btn_cancel').text(vtoy_cur_language.STR_BTN_CANCEL);\r
+        \r
+        $('h4#part_config_dlg_title').text(vtoy_cur_language.STR_MENU_PART_CFG);\r
+    }\r
+    \r
+    function select_language_by_name(name, sendback) {\r
+        for (var j = 0; j < vtoy_language_data.length; j++) {\r
+            if (vtoy_language_data[j].name.indexOf(name) == 0) {\r
+                vtoy_cur_language = vtoy_language_data[j];\r
+                update_language(vtoy_cur_lang_index, j);\r
+                \r
+                if (sendback > 0) {\r
+                    var idx = name.indexOf(' (');\r
+                    if (idx >= 0) {\r
+                        callVtoy({method : 'sel_language', token:vtoy_current_token, language:name.substr(0, idx)});\r
+                    } else {\r
+                        callVtoy({method : 'sel_language', token:vtoy_current_token, language:name});\r
+                    }\r
+                }\r
+                break;\r
+            }\r
+        }\r
+    }\r
+    \r
+    function select_language_by_index(index, sendback) {\r
+        if (index < vtoy_language_data.length) {\r
+            vtoy_cur_language = vtoy_language_data[index];\r
+            update_language(vtoy_cur_lang_index, index);            \r
+            \r
+            if (sendback > 0) {\r
+                var idx = vtoy_cur_language.name.indexOf(' (');\r
+                if (idx >= 0) {\r
+                    callVtoy({method : 'sel_language', token:vtoy_current_token, language:vtoy_cur_language.name.substr(0, idx)});\r
+                } else {\r
+                    callVtoy({method : 'sel_language', token:vtoy_current_token, language:vtoy_cur_language.name});\r
+                }\r
+            }\r
+        }\r
+    }\r
+    \r
+    function on_language_click(index) {\r
+        if (index != vtoy_cur_lang_index) {\r
+            select_language_by_index(index, 1);\r
+        }\r
+    }\r
+    \r
+    function on_select_mbr() {\r
+        vtoy_selected_part_style = 0;\r
+        $('span#vtoy_mbr_check').css("visibility","visible");\r
+        $('span#vtoy_gpt_check').css("visibility","hidden");\r
+        $('span#vtoy_local_part_style').text('MBR');        \r
+    }\r
+    \r
+    function on_select_mbr_click() {\r
+        on_select_mbr();\r
+        callVtoy({method : 'sel_partstyle', token:vtoy_current_token,  partstyle:0});\r
+    }\r
+    \r
+    function on_select_gpt() {    \r
+        vtoy_selected_part_style = 1;\r
+        $('span#vtoy_mbr_check').css("visibility","hidden");\r
+        $('span#vtoy_gpt_check').css("visibility","visible");\r
+        $('span#vtoy_local_part_style').text('GPT');\r
+    }\r
+    \r
+    function on_select_gpt_click() {    \r
+        on_select_gpt();        \r
+        callVtoy({method : 'sel_partstyle', token:vtoy_current_token, partstyle:1});\r
+    }\r
+\r
+    function secure_boot_check(secure) {\r
+        if (secure > 0) {\r
+            $('span#vtoy_check_secure_boot').css("visibility","visible");\r
+            $('span#vtoy_select_secure_icon').css("visibility","visible");\r
+        } else {\r
+            $('span#vtoy_check_secure_boot').css("visibility","hidden");\r
+            $('span#vtoy_select_secure_icon').css("visibility","hidden");\r
+        }\r
+    }\r
+\r
+    function on_secure_boot() {\r
+        var secure;\r
+        if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+            secure = 1;\r
+        } else {\r
+            secure = 0;\r
+        }\r
+        secure_boot_check(1 - secure);\r
+    }\r
+    \r
+    function on_show_all_device() {\r
+        if ($('span#vtoy_check_show_all_dev').css("visibility") === 'visible') {\r
+            $('span#vtoy_check_show_all_dev').css("visibility", "hidden");\r
+        } else {\r
+            $('span#vtoy_check_show_all_dev').css("visibility", "visible");\r
+        }\r
+        get_and_fill_dev_list();        \r
+    }\r
+    \r
+    \r
+    var vtoy_part_align_4kb_tmp;\r
+    var vtoy_preserve_space_checkbox_tmp;\r
+    var vtoy_preserve_space_tmp;\r
+    var vtoy_space_unit_dropbox_tmp;\r
+    \r
+    function on_config_partition() {        \r
+        if (vtoy_chrome_app_mode) {\r
+            $("#vtoy_set_part_cfg_modal_dlg").css("width", "520px");\r
+            $("#vtoy_set_part_cfg_modal_dlg").css("margin-top", "-30px");\r
+        } else {\r
+            $("#vtoy_set_part_cfg_modal_dlg").css("width", "540px");\r
+            \r
+            if (document.body.clientWidth > 550) {\r
+                $("#vtoy_set_part_cfg_modal_dlg").css("left", (document.body.clientWidth - 550) / 2);\r
+            }\r
+            \r
+            if (document.body.clientHeight > 400) {\r
+                $("#vtoy_set_part_cfg_modal_dlg").css("top", (document.body.clientHeight - 400) / 2);\r
+            }\r
+        }\r
+        \r
+        vtoy_part_align_4kb_tmp = $('#vtoy_part_align_4kb').prop("checked");\r
+        vtoy_preserve_space_checkbox_tmp = $('#vtoy_preserve_space_checkbox').prop("checked");\r
+        vtoy_preserve_space_tmp = $('#vtoy_preserve_space').val();\r
+        vtoy_space_unit_dropbox_tmp = $('#vtoy_space_unit_dropbox').val();\r
+        \r
+        $('#vtoy_set_part_cfg_modal').modal({backdrop: 'static', keyboard: false});\r
+    }\r
+    \r
+    $("#vtoy_modal_btn_ok").click(function(){  \r
+        $("#vtoy_set_part_cfg_modal").modal('hide');\r
+    });\r
+    \r
+    $("#vtoy_modal_btn_cancel").click(function(){  \r
+        $("#vtoy_set_part_cfg_modal").modal('hide');\r
+        $('#vtoy_part_align_4kb').prop("checked", vtoy_part_align_4kb_tmp);\r
+        $('#vtoy_preserve_space_checkbox').prop("checked", vtoy_preserve_space_checkbox_tmp);\r
+        $('#vtoy_preserve_space').val(vtoy_preserve_space_tmp);\r
+        $('#vtoy_space_unit_dropbox').val(vtoy_space_unit_dropbox_tmp);\r
+    });\r
+    \r
+    function on_enable_preserve_space() {\r
+        if ($('#vtoy_preserve_space_checkbox').is(':checked')) {\r
+            $('#vtoy_preserve_space').attr("disabled",false);\r
+            $('#vtoy_space_unit_dropbox').attr("disabled",false);\r
+        } else {\r
+            $('#vtoy_preserve_space').attr("disabled",true);\r
+            $('#vtoy_space_unit_dropbox').attr("disabled",true);\r
+        }\r
+    }\r
+        \r
+    function ResizeWindow() {\r
+        //console.log(window.screen.width + ' [x1] ' + window.screen.height);        \r
+        //console.log(vtoy_app_width + ' [x2] ' + vtoy_app_height);\r
+        //console.log((window.screen.width - vtoy_app_width) / 2 + ' [x3] ' + (window.screen.height - vtoy_app_height) / 2);        \r
+        window.moveTo((window.screen.width - vtoy_app_width) / 2, (window.screen.height - vtoy_app_height) / 2);\r
+        window.resizeTo(vtoy_app_width, vtoy_app_height);\r
+    }\r
+\r
+    function MoveMainDivToCenter() {\r
+        $('#vtoy_main_div').css("left", (document.body.clientWidth - 550) / 2);\r
+        $('#vtoy_main_div').css("top",(document.body.clientHeight - 400) / 2);\r
+    }\r
+\r
+    // disable F5\r
+    function disableF5(e) {\r
+        if ((e.which || e.keyCode) == 116) {\r
+            e.preventDefault(); \r
+        };\r
+    }\r
+    \r
+    function on_dev_sel_change() {\r
+        var index = $("#vtoy_dev_list").val();\r
+        \r
+        $('span#vtoy_dev_secure_icon').css("visibility","hidden");\r
+        \r
+        if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+            return;\r
+        }\r
+        \r
+        if (vtoy_cur_dev_list[index].vtoy_valid > 0) {\r
+            $('span#vtoy_dev_ver').text(vtoy_cur_dev_list[index].vtoy_ver);\r
+            $('span#vtoy_dev_part_style').text(vtoy_cur_dev_list[index].vtoy_partstyle ? 'GPT' : 'MBR');\r
+            if (vtoy_cur_dev_list[index].vtoy_secure_boot) {\r
+                $('span#vtoy_dev_secure_icon').css("visibility","visible");\r
+            }\r
+\r
+            secure_boot_check(vtoy_cur_dev_list[index].vtoy_secure_boot);\r
+            \r
+            $('button#VtoyBtnUpdate').prop("disabled", false);\r
+            \r
+        } else {\r
+            $('span#vtoy_dev_ver').text('');\r
+            $('span#vtoy_dev_part_style').text('');\r
+            $('button#VtoyBtnUpdate').prop("disabled", true);\r
+        }\r
+    }\r
+    \r
+    $("#vtoy_dev_list").change(on_dev_sel_change);\r
+    \r
+    function ventoy_display_alert(type, vmsg) {\r
+        var titlestr;\r
+        var msgstr;\r
+        var message;\r
+        \r
+        if (type === 'error') {\r
+            titlestr = '<span class="fa  fa-minus-circle" style="color:#dd4b39; font-weight:bold;"> ' + vtoy_cur_language.STR_ERROR + '</span>';\r
+        } else if (type === 'warning') {\r
+            titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+        } else {\r
+            titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+        }\r
+        \r
+        msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vmsg + '</span>';\r
+        message = msgstr.replace("#@", "<br/>");\r
+        \r
+        Modal.alert({title:titlestr, msg:message, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL });\r
+    }\r
+    \r
+    function set_progress_bar(percent) {\r
+        var per = percent + '%';\r
+        \r
+        $('#vtoy_progress_bar').css('width', per);\r
+        \r
+        if (percent === 0) {\r
+            $('span#vtoy_status_title').text(vtoy_cur_language.STR_STATUS);\r
+        } else {\r
+            var status = vtoy_cur_language.STR_STATUS;\r
+            var idx = status.indexOf('- ');\r
+            \r
+            if (idx >= 0) {\r
+                $('span#vtoy_status_title').text(status.substr(0, idx + 2) + per);\r
+            }\r
+        }\r
+    }\r
+    \r
+    \r
+    function progressDisableItem(in_progress) {\r
+        $('button#VtoyBtnInstall').prop("disabled", in_progress);\r
+        $('button#VtoyBtnUpdate').prop("disabled", in_progress);\r
+    }\r
+    \r
+    function queryProgress(op) {\r
+        callVtoySync({\r
+            method : 'get_percent',\r
+            token : vtoy_current_token\r
+        }, function(data) {\r
+            if (data.result === 'success') {\r
+                set_progress_bar(data.percent);                \r
+                if (data.percent === 100) {\r
+                    var titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+                    var msgstr;\r
+\r
+                    if (op === 1) {\r
+                        msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_INSTALL_SUCCESS + '</span>';\r
+                    } else {\r
+                        msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_UPDATE_SUCCESS + '</span>';\r
+                    }\r
+                    var message = msgstr.replace("#@", "<br/>");\r
+\r
+                    callVtoySync({\r
+                        method : 'refresh_device',\r
+                        token : vtoy_current_token\r
+                    }, function(data) {            \r
+                        get_and_fill_dev_list();\r
+                        \r
+                        for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+                            if (vtoy_cur_dev_list[i].name === vtoy_cur_process_disk_name) {\r
+                                $("#vtoy_dev_list").val(i);\r
+                                on_dev_sel_change();\r
+                                break;\r
+                            }                            \r
+                        }\r
+                    });\r
+                    \r
+                    Modal.alert({title:titlestr, msg:message, btnok:vtoy_cur_language.STR_BTN_OK }).on(function(e) {\r
+                        vtoy_in_progress = false;\r
+                        progressDisableItem(vtoy_in_progress);\r
+                        set_progress_bar(0);\r
+                    });\r
+                } else {\r
+                    setTimeout(function() {\r
+                        queryProgress(op);\r
+                    }, 300);\r
+                }                \r
+            } else {\r
+                \r
+                if (data.result === 'mbr2tb') {\r
+                    ventoy_display_alert('error', vtoy_cur_language.STR_DISK_2TB_MBR_ERROR);\r
+                } else if (data.result === 'reserve_invalid') {\r
+                    ventoy_display_alert('error', vtoy_cur_language.STR_SPACE_VAL_INVALID);\r
+                } else {\r
+                    ventoy_display_alert('error', (op === 1) ? vtoy_cur_language.STR_INSTALL_FAILED : vtoy_cur_language.STR_UPDATE_FAILED);\r
+                }\r
+                \r
+                vtoy_in_progress = false;\r
+                progressDisableItem(vtoy_in_progress);\r
+                set_progress_bar(0);\r
+            }\r
+        });\r
+    }\r
+    \r
+    function install_ventoy(index, reservesize) {\r
+        var secureboot;\r
+        var curDev = vtoy_cur_dev_list[index];\r
+        var align = $('#vtoy_part_align_4kb').prop("checked");\r
+        \r
+        vtoy_cur_process_disk_name = curDev.name;\r
+        \r
+        if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+            secureboot = 1;\r
+        } else {\r
+            secureboot = 0;\r
+        }\r
+        \r
+        callVtoySync({\r
+            method : 'install',\r
+            token : vtoy_current_token,\r
+            disk : curDev.name,\r
+            partstyle : vtoy_selected_part_style,\r
+            secure_boot : secureboot,\r
+            align_4kb : align ? 1 : 0,\r
+            reserve_space : reservesize + ''            \r
+        }, function(data) {            \r
+            if (data.result === 'success') {\r
+                vtoy_in_progress = true;\r
+                progressDisableItem(vtoy_in_progress);\r
+                queryProgress(1);\r
+            } else {\r
+                ventoy_display_alert('error', vtoy_cur_language.STR_INSTALL_FAILED);\r
+            }\r
+        });\r
+    }\r
+    \r
+    \r
+    function on_vtoy_install() {\r
+        var model;\r
+        var reserve;\r
+        var titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+        var msgstr1 = vtoy_cur_language.STR_INSTALL_TIP.replace("#@", "<br/>");\r
+        var msgstr2 = vtoy_cur_language.STR_INSTALL_TIP2.replace("#@", "<br/>");\r
+        var index = $("#vtoy_dev_list").val();\r
+        \r
+        if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+            return;\r
+        }\r
+        \r
+        if (vtoy_in_progress) {\r
+            return;\r
+        }\r
+        \r
+        if ($('#vtoy_preserve_space_checkbox').is(':checked')) {\r
+            var valx = $('#vtoy_preserve_space').val();\r
+            if (valx > 0) {\r
+                if ($('#vtoy_space_unit_dropbox').val() > 0) {\r
+                    reserve = valx * 1024 * 1024;\r
+                } else {\r
+                    reserve = valx * 1024 * 1024 * 1024;\r
+                } \r
+            }\r
+        } else {\r
+            reserve = 0;\r
+        }\r
+        \r
+        var curDev = vtoy_cur_dev_list[index];\r
+        model = curDev.name + '&nbsp;&nbsp;[' + curDev.size + ']&nbsp;&nbsp;' + curDev.model + '<br/>';        \r
+        var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+        var msgstrex2 = '<span style="font-size:14px; font-weight:bold; color:#f39c12;"> ' + model + msgstr2 + '</span>';\r
+        \r
+        Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+            if (e) {\r
+                Modal.confirm({title:titlestr, msg:msgstrex2, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e1) {\r
+                    if (e1) {\r
+                        install_ventoy(index, reserve);\r
+                    }\r
+                });\r
+            }\r
+        });\r
+    }\r
+\r
+\r
+    function update_ventoy(index) {\r
+        var secureboot;\r
+        var curDev = vtoy_cur_dev_list[index];\r
+        \r
+        vtoy_cur_process_disk_name = curDev.name;\r
+        \r
+        if ($('span#vtoy_check_secure_boot').css("visibility") === 'visible') {\r
+            secureboot = 1;\r
+        } else {\r
+            secureboot = 0;\r
+        }\r
+        \r
+        callVtoySync({\r
+            method : 'update',\r
+            token : vtoy_current_token,\r
+            disk : curDev.name,\r
+            secure_boot : secureboot\r
+        }, function(data) {            \r
+            if (data.result === 'success') {\r
+                vtoy_in_progress = true;\r
+                progressDisableItem(vtoy_in_progress);\r
+                queryProgress(2);\r
+            } else {\r
+                ventoy_display_alert('error', vtoy_cur_language.STR_UPDATE_FAILED);\r
+            }\r
+        });\r
+    }\r
+\r
+    function on_vtoy_update() {\r
+        var model;\r
+        var reserve;\r
+        var titlestr = '<span class="fa fa-info" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+        var msgstr1 = vtoy_cur_language.STR_UPDATE_TIP.replace("#@", "<br/>");\r
+        var index = $("#vtoy_dev_list").val();\r
+        \r
+        if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+            return;\r
+        }\r
+        \r
+        if (vtoy_in_progress) {\r
+            return;\r
+        }\r
+        \r
+        var curDev = vtoy_cur_dev_list[index];\r
+        model = curDev.name + '&nbsp;&nbsp;[' + curDev.size + ']&nbsp;&nbsp;' + curDev.model + '<br/>';        \r
+        var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+        \r
+        Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+            if (e) {\r
+                update_ventoy(index);\r
+            }\r
+        });\r
+    }\r
+    \r
+    function on_clean_ventoy() {\r
+        var model;\r
+        var reserve;\r
+        var titlestr = '<span class="fa fa-warning" style="color:#f39c12; font-weight:bold;"> ' + vtoy_cur_language.STR_WARNING + '</span>';\r
+        var msgstr1 = vtoy_cur_language.STR_INSTALL_TIP.replace("#@", "<br/>");\r
+        var msgstr2 = vtoy_cur_language.STR_INSTALL_TIP2.replace("#@", "<br/>");\r
+        var index = $("#vtoy_dev_list").val();\r
+        \r
+        if (vtoy_cur_dev_list.length <= 0 || index < 0 || index >= vtoy_cur_dev_list.length) {\r
+            return;\r
+        }\r
+        \r
+        if (vtoy_in_progress) {\r
+            return;\r
+        }\r
+        \r
+        var curDev = vtoy_cur_dev_list[index];\r
+        model = curDev.name + '&nbsp;&nbsp;[' + curDev.size + ']&nbsp;&nbsp;' + curDev.model + '<br/>';        \r
+        var msgstrex1 = '<span style="font-size:14px; font-weight:bold;"> ' + model + msgstr1 + '</span>';\r
+        var msgstrex2 = '<span style="font-size:14px; font-weight:bold; color:#f39c12;"> ' + model + msgstr2 + '</span>';\r
+        \r
+        Modal.confirm({title:titlestr, msg:msgstrex1, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e) {\r
+            if (e) {\r
+                Modal.confirm({title:titlestr, msg:msgstrex2, btnok:vtoy_cur_language.STR_BTN_OK, btncl:vtoy_cur_language.STR_BTN_CANCEL }).on(function(e1) {\r
+                    if (e1) {\r
+                        callVtoySync({\r
+                            method : 'clean',\r
+                            token : vtoy_current_token,\r
+                            disk : curDev.name                    \r
+                        }, function(data) {            \r
+                            if (data.result === 'success') {\r
+                                ventoy_display_alert('success', vtoy_cur_language.STR_CLEAR_SUCCESS);\r
+                                \r
+                                callVtoySync({\r
+                                    method : 'refresh_device',\r
+                                    token : vtoy_current_token\r
+                                }, function(data) {            \r
+                                    get_and_fill_dev_list();\r
+                                    \r
+                                    for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+                                        if (vtoy_cur_dev_list[i].name === curDev.name) {\r
+                                            $("#vtoy_dev_list").val(i);\r
+                                            on_dev_sel_change();\r
+                                            break;\r
+                                        }                            \r
+                                    }\r
+                                });\r
+                                \r
+                            } else {\r
+                                ventoy_display_alert('error', vtoy_cur_language.STR_CLEAR_FAILED);\r
+                            }\r
+                        });\r
+                    }\r
+                });\r
+            }\r
+        });\r
+    }\r
+    \r
+    \r
+    \r
+    function fill_dev_list_dropbox() {\r
+        var model;        \r
+        $('#vtoy_dev_list').empty();\r
+        $('span#vtoy_dev_secure_icon').css("visibility","hidden");\r
+        \r
+        for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+            model = vtoy_cur_dev_list[i].name + '&nbsp;&nbsp;[' + vtoy_cur_dev_list[i].size + ']&nbsp;&nbsp;' + vtoy_cur_dev_list[i].model;            \r
+            $("#vtoy_dev_list").append("<option value='" + i + "'>" + model + "</option>");\r
+        }\r
+        \r
+        $('span#vtoy_dev_ver').text('');\r
+        $('span#vtoy_dev_part_style').text('');\r
+        \r
+        if (vtoy_cur_dev_list.length > 0) {\r
+            if (vtoy_cur_dev_list[0].vtoy_valid > 0) {\r
+                $('span#vtoy_dev_ver').text(vtoy_cur_dev_list[0].vtoy_ver);\r
+                $('span#vtoy_dev_part_style').text(vtoy_cur_dev_list[0].vtoy_partstyle ? 'GPT' : 'MBR');\r
+                if (vtoy_cur_dev_list[0].vtoy_secure_boot) {\r
+                    $('span#vtoy_dev_secure_icon').css("visibility","visible");\r
+                }\r
+                \r
+                secure_boot_check(vtoy_cur_dev_list[0].vtoy_secure_boot);\r
+                \r
+                $('button#VtoyBtnUpdate').prop("disabled", false);\r
+            } else {\r
+                $('button#VtoyBtnUpdate').prop("disabled", true);\r
+            }\r
+            $('button#VtoyBtnInstall').prop("disabled", false);\r
+        } else {\r
+            $('button#VtoyBtnInstall').prop("disabled", true);\r
+            $('button#VtoyBtnUpdate').prop("disabled", true);\r
+        }\r
+    }\r
+    \r
+    function get_and_fill_dev_list() {\r
+        var showall = 0;\r
+        if ($('span#vtoy_check_show_all_dev').css("visibility") === 'visible') {\r
+            showall = 1;\r
+        }\r
+    \r
+        callVtoySync({\r
+            method : 'get_dev_list',\r
+            alldev : showall,\r
+            token : vtoy_current_token\r
+        }, function(data) {            \r
+            vtoy_cur_dev_list = data.list;\r
+            fill_dev_list_dropbox();\r
+        });\r
+    }\r
+    \r
+    function on_click_refresh_device() {\r
+        if (vtoy_in_progress === true) {\r
+            return;\r
+        }\r
+        callVtoySync({\r
+            method : 'refresh_device',\r
+            token : vtoy_current_token\r
+        }, function(data) {            \r
+            get_and_fill_dev_list();\r
+        });\r
+    }\r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
+    //SCRIPT_DEL_THIS $(document).on("keydown", disableF5);\r
+    //SCRIPT_DEL_THIS $(document).on("contextmenu",function(e){ return false; });\r
+\r
+    $('#refresh_dev_img').click(on_click_refresh_device);\r
+    $('#vtoy_part_align_4kb').prop("checked", true);\r
+    \r
+    if (vtoy_chrome_app_mode) {\r
+        window.onresize = function() {\r
+            ResizeWindow();\r
+        }\r
+        ResizeWindow();\r
+    } else {\r
+        $('#vtoy_main_div').css("border", '2px solid #f4f4f4');\r
+        window.onresize = function() {\r
+            MoveMainDivToCenter();\r
+        }\r
+        MoveMainDivToCenter();\r
+    }\r
+\r
+    sort_language_list();\r
+    fill_language_list();\r
+\r
+    if (navigator.language) {\r
+        vtoy_client_language = navigator.language.toLowerCase();\r
+    }\r
+    else {\r
+        vtoy_client_language = navigator.browserLanguage.toLowerCase();\r
+    }\r
+    \r
+    if (vtoy_client_language === 'zh-cn') {\r
+        select_language_by_index(0, 0);\r
+    } else {\r
+        select_language_by_name('English (English)', 0);\r
+    }\r
+    \r
+    on_select_mbr();\r
+    secure_boot_check(0);\r
+    \r
+    on_enable_preserve_space();\r
+    \r
+    callVtoySync({\r
+        method : 'sysinfo'\r
+    }, function(data) {  \r
+        vtoy_current_token = data.token;        \r
+        if (data.language.length > 0) {\r
+            select_language_by_name(data.language, 0);\r
+        }\r
+        \r
+        if (data.busy === true) {\r
+            vtoy_cur_process_disk_name = data.process_disk;\r
+            vtoy_in_progress = true;\r
+        } else {\r
+            vtoy_in_progress = false;\r
+        }\r
+        \r
+        if (data.partstyle == 1) {\r
+            on_select_gpt();\r
+        } else {\r
+            on_select_mbr();\r
+        }\r
+        \r
+        $('span#vtoy_local_ver').text(data.ventoy_ver);\r
+        \r
+        callVtoySync({\r
+            method : 'refresh_device',\r
+            token : vtoy_current_token\r
+        }, function(data) {            \r
+            get_and_fill_dev_list();\r
+        });\r
+        \r
+        if (vtoy_in_progress) {\r
+            for (var i = 0; i < vtoy_cur_dev_list.length; i++) {\r
+                if (vtoy_cur_dev_list[i].name === vtoy_cur_process_disk_name) {\r
+                    $("#vtoy_dev_list").val(i);\r
+                    on_dev_sel_change();\r
+                    break;\r
+                }                            \r
+            }\r
+            \r
+            progressDisableItem(vtoy_in_progress);\r
+            if (data.process_type === 'install') {\r
+                queryProgress(1);\r
+            } else if (data.process_type === 'update') {\r
+                queryProgress(2);\r
+            }\r
+        }\r
+    });\r
+    \r
+</script>\r
+</body>\r
+\r
+</html>\r
diff --git a/LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css b/LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css
new file mode 100644 (file)
index 0000000..fd35437
--- /dev/null
@@ -0,0 +1,8 @@
+/**@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic); **/
+/*!
+ *   AdminLTE v2.3.0
+ *   Author: Almsaeed Studio
+ *   Website: Almsaeed Studio <http://almsaeedstudio.com>
+ *   License: Open source - MIT
+ *           Please visit http://opensource.org/licenses/MIT for more information
+!*/html,body{/*min-height:100%*/}.layout-boxed html,.layout-boxed body{height:100%}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif;font-weight:400;overflow-x:hidden;overflow-y:auto}.wrapper{min-height:100%;position:static;overflow:hidden}.wrapper:before,.wrapper:after{content:" ";display:table}.wrapper:after{clear:both}.layout-boxed .wrapper{max-width:1250px;margin:0 auto;min-height:100%;box-shadow:0 0 8px rgba(0,0,0,0.5);position:relative}.layout-boxed{background:url('../img/boxed-bg.jpg') repeat fixed}.content-wrapper,.right-side,.main-footer{-webkit-transition:-webkit-transform .3s ease-in-out,margin .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,margin .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,margin .3s ease-in-out;transition:transform .3s ease-in-out,margin .3s ease-in-out;margin-left:230px;z-index:820}.layout-top-nav .content-wrapper,.layout-top-nav .right-side,.layout-top-nav .main-footer{margin-left:0}@media (max-width:767px){.content-wrapper,.right-side,.main-footer{margin-left:0}}@media (min-width:768px){.sidebar-collapse .content-wrapper,.sidebar-collapse .right-side,.sidebar-collapse .main-footer{margin-left:0}}@media (max-width:767px){.sidebar-open .content-wrapper,.sidebar-open .right-side,.sidebar-open .main-footer{-webkit-transform:translate(230px, 0);-ms-transform:translate(230px, 0);-o-transform:translate(230px, 0);transform:translate(230px, 0)}}.content-wrapper,.right-side{min-height:100%;background-color:#ecf0f5;z-index:800}.main-footer{background:#fff;padding:15px;color:#444;border-top:1px solid #d2d6de}.fixed .main-header,.fixed .main-sidebar,.fixed .left-side{position:fixed}.fixed .main-header{top:0;right:0;left:0}.fixed .content-wrapper,.fixed .right-side{padding-top:50px}@media (max-width:767px){.fixed .content-wrapper,.fixed .right-side{padding-top:100px}}.fixed.layout-boxed .wrapper{max-width:100%}body.hold-transition .content-wrapper,body.hold-transition .right-side,body.hold-transition .main-footer,body.hold-transition .main-sidebar,body.hold-transition .left-side,body.hold-transition .main-header>.navbar,body.hold-transition .main-header .logo{-webkit-transition:none;-o-transition:none;transition:none}.content{min-height:250px;padding:15px;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif}a{color:#3c8dbc}a:hover,a:active,a:focus{outline:none;text-decoration:none;color:#72afd2}.page-header{margin:10px 0 20px 0;font-size:22px}.page-header>small{color:#666;display:block;margin-top:5px}.main-header{position:relative;max-height:100px;z-index:1030}.main-header>.navbar{-webkit-transition:margin-left .3s ease-in-out;-o-transition:margin-left .3s ease-in-out;transition:margin-left .3s ease-in-out;margin-bottom:0;margin-left:230px;border:none;min-height:50px;border-radius:0}.layout-top-nav .main-header>.navbar{margin-left:0}.main-header #navbar-search-input.form-control{background:rgba(255,255,255,0.2);border-color:transparent}.main-header #navbar-search-input.form-control:focus,.main-header #navbar-search-input.form-control:active{border-color:rgba(0,0,0,0.1);background:rgba(255,255,255,0.9)}.main-header #navbar-search-input.form-control::-moz-placeholder{color:#ccc;opacity:1}.main-header #navbar-search-input.form-control:-ms-input-placeholder{color:#ccc}.main-header #navbar-search-input.form-control::-webkit-input-placeholder{color:#ccc}.main-header .navbar-custom-menu,.main-header .navbar-right{float:right}@media (max-width:991px){.main-header .navbar-custom-menu a,.main-header .navbar-right a{color:inherit;background:transparent}}@media (max-width:767px){.main-header .navbar-right{float:none}.navbar-collapse .main-header .navbar-right{margin:7.5px -15px}.main-header .navbar-right>li{color:inherit;border:0}}.main-header .sidebar-toggle{float:left;background-color:transparent;background-image:none;padding:15px 15px;font-family:fontAwesome}.main-header .sidebar-toggle:before{content:"\f0c9"}.main-header .sidebar-toggle:hover{color:#fff}.main-header .sidebar-toggle:focus,.main-header .sidebar-toggle:active{background:transparent}.main-header .sidebar-toggle .icon-bar{display:none}.main-header .navbar .nav>li.user>a>.fa,.main-header .navbar .nav>li.user>a>.glyphicon,.main-header .navbar .nav>li.user>a>.ion{margin-right:5px}.main-header .navbar .nav>li>a>.label{position:absolute;top:9px;right:7px;text-align:center;font-size:9px;padding:2px 3px;line-height:.9}.main-header .logo{-webkit-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out;display:block;float:left;height:50px;font-size:20px;line-height:50px;text-align:center;width:230px;font-family:'Arial','Microsoft YaHei','ºÚÌå','ËÎÌå',sans-serif;padding:0 15px;font-weight:300;overflow:hidden}.main-header .logo .logo-lg{display:block}.main-header .logo .logo-mini{display:none}.main-header .navbar-brand{color:#fff}.content-header{position:relative;padding:15px 15px 0 15px}.content-header>h1{margin:0;font-size:24px}.content-header>h1>small{font-size:15px;display:inline-block;padding-left:4px;font-weight:300}.content-header>.breadcrumb{float:right;background:transparent;margin-top:0;margin-bottom:0;font-size:12px;padding:7px 5px;position:absolute;top:15px;right:10px;border-radius:2px}.content-header>.breadcrumb>li>a{color:#444;text-decoration:none;display:inline-block}.content-header>.breadcrumb>li>a>.fa,.content-header>.breadcrumb>li>a>.glyphicon,.content-header>.breadcrumb>li>a>.ion{margin-right:5px}.content-header>.breadcrumb>li+li:before{content:'>\00a0'}@media (max-width:991px){.content-header>.breadcrumb{position:relative;margin-top:5px;top:0;right:0;float:none;background:#d2d6de;padding-left:10px}.content-header>.breadcrumb li:before{color:#97a0b3}}.navbar-toggle{color:#fff;border:0;margin:0;padding:15px 15px}@media (max-width:991px){.navbar-custom-menu .navbar-nav>li{float:left}.navbar-custom-menu .navbar-nav{margin:0;float:left}.navbar-custom-menu .navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px}}@media (max-width:767px){.main-header{position:relative}.main-header .logo,.main-header .navbar{width:100%;float:none}.main-header .navbar{margin:0}.main-header .navbar-custom-menu{float:right}}@media (max-width:991px){.navbar-collapse.pull-left{float:none!important}.navbar-collapse.pull-left+.navbar-custom-menu{display:block;position:absolute;top:0;right:40px}}.main-sidebar,.left-side{position:absolute;top:0;left:0;padding-top:50px;min-height:100%;width:230px;z-index:810;-webkit-transition:-webkit-transform .3s ease-in-out,width .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,width .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,width .3s ease-in-out;transition:transform .3s ease-in-out,width .3s ease-in-out}@media (max-width:767px){.main-sidebar,.left-side{padding-top:100px}}@media (max-width:767px){.main-sidebar,.left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (min-width:768px){.sidebar-collapse .main-sidebar,.sidebar-collapse .left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (max-width:767px){.sidebar-open .main-sidebar,.sidebar-open .left-side{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}}.sidebar{padding-bottom:10px}.sidebar-form input:focus{border-color:transparent}.user-panel{position:relative;width:100%;padding:10px;overflow:hidden}.user-panel:before,.user-panel:after{content:" ";display:table}.user-panel:after{clear:both}.user-panel>.image>img{width:100%;max-width:45px;height:auto}.user-panel>.info{padding:5px 5px 5px 15px;line-height:1;position:absolute;left:55px}.user-panel>.info>p{font-weight:600;margin-bottom:9px}.user-panel>.info>a{text-decoration:none;padding-right:5px;margin-top:3px;font-size:11px}.user-panel>.info>a>.fa,.user-panel>.info>a>.ion,.user-panel>.info>a>.glyphicon{margin-right:3px}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu>li{position:relative;margin:0;padding:0}.sidebar-menu>li>a{padding:12px 5px 12px 15px;display:block}.sidebar-menu>li>a>.fa,.sidebar-menu>li>a>.glyphicon,.sidebar-menu>li>a>.ion{width:20px}.sidebar-menu>li .label,.sidebar-menu>li .badge{margin-top:3px;margin-right:5px}.sidebar-menu li.header{padding:10px 25px 10px 15px;font-size:12px}.sidebar-menu li>a>.fa-angle-left{width:auto;height:auto;padding:0;margin-right:10px;margin-top:3px}.sidebar-menu li.active>a>.fa-angle-left{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu li.active>.treeview-menu{display:block}.sidebar-menu .treeview-menu{display:none;list-style:none;padding:0;margin:0;padding-left:5px}.sidebar-menu .treeview-menu .treeview-menu{padding-left:20px}.sidebar-menu .treeview-menu>li{margin:0}.sidebar-menu .treeview-menu>li>a{padding:5px 5px 5px 15px;display:block;font-size:14px}.sidebar-menu .treeview-menu>li>a>.fa,.sidebar-menu .treeview-menu>li>a>.glyphicon,.sidebar-menu .treeview-menu>li>a>.ion{width:20px}.sidebar-menu .treeview-menu>li>a>.fa-angle-left,.sidebar-menu .treeview-menu>li>a>.fa-angle-down{width:auto}@media (min-width:768px){.sidebar-mini.sidebar-collapse .content-wrapper,.sidebar-mini.sidebar-collapse .right-side,.sidebar-mini.sidebar-collapse .main-footer{margin-left:50px!important;z-index:840}.sidebar-mini.sidebar-collapse .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);width:50px!important;z-index:850}.sidebar-mini.sidebar-collapse .sidebar-menu>li{position:relative}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a{margin-right:0}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span{border-top-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:not(.treeview)>a>span{border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{padding-top:5px;padding-bottom:5px;border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span:not(.pull-right),.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{display:block!important;position:absolute;width:180px;left:50px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span{top:0;margin-left:-3px;padding:12px 5px 12px 20px;background-color:inherit}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{top:44px;margin-left:0}.sidebar-mini.sidebar-collapse .main-sidebar .user-panel>.info,.sidebar-mini.sidebar-collapse .sidebar-form,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span,.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu li.header{display:none!important;-webkit-transform:translateZ(0)}.sidebar-mini.sidebar-collapse .main-header .logo{width:50px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-mini{display:block;margin-left:-15px;margin-right:-15px;font-size:18px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-lg{display:none}.sidebar-mini.sidebar-collapse .main-header .navbar{margin-left:50px}}.sidebar-menu,.main-sidebar .user-panel,.sidebar-menu>li.header{white-space:nowrap;overflow:hidden}.sidebar-menu:hover{overflow:visible}.sidebar-form,.sidebar-menu>li.header{overflow:hidden;text-overflow:clip}.sidebar-menu li>a{position:relative}.sidebar-menu li>a>.pull-right{position:absolute;top:50%;right:10px;margin-top:-7px}.control-sidebar-bg{position:fixed;z-index:1000;bottom:0}.control-sidebar-bg,.control-sidebar{top:0;right:-230px;width:230px;-webkit-transition:right .3s ease-in-out;-o-transition:right .3s ease-in-out;transition:right .3s ease-in-out}.control-sidebar{position:absolute;padding-top:50px;z-index:1010}@media (max-width:768px){.control-sidebar{padding-top:100px}}.control-sidebar>.tab-content{padding:10px 15px}.control-sidebar.control-sidebar-open,.control-sidebar.control-sidebar-open+.control-sidebar-bg{right:0}.control-sidebar-open .control-sidebar-bg,.control-sidebar-open .control-sidebar{right:0}@media (min-width:768px){.control-sidebar-open .content-wrapper,.control-sidebar-open .right-side,.control-sidebar-open .main-footer{margin-right:230px}}.nav-tabs.control-sidebar-tabs>li:first-of-type>a,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:hover,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:focus{border-left-width:0}.nav-tabs.control-sidebar-tabs>li>a{border-radius:0}.nav-tabs.control-sidebar-tabs>li>a,.nav-tabs.control-sidebar-tabs>li>a:hover{border-top:none;border-right:none;border-left:1px solid transparent;border-bottom:1px solid transparent}.nav-tabs.control-sidebar-tabs>li>a .icon{font-size:16px}.nav-tabs.control-sidebar-tabs>li.active>a,.nav-tabs.control-sidebar-tabs>li.active>a:hover,.nav-tabs.control-sidebar-tabs>li.active>a:focus,.nav-tabs.control-sidebar-tabs>li.active>a:active{border-top:none;border-right:none;border-bottom:none}@media (max-width:768px){.nav-tabs.control-sidebar-tabs{display:table}.nav-tabs.control-sidebar-tabs>li{display:table-cell}}.control-sidebar-heading{font-weight:400;font-size:16px;padding:10px 0;margin-bottom:10px}.control-sidebar-subheading{display:block;font-weight:400;font-size:14px}.control-sidebar-menu{list-style:none;padding:0;margin:0 -15px}.control-sidebar-menu>li>a{display:block;padding:10px 15px}.control-sidebar-menu>li>a:before,.control-sidebar-menu>li>a:after{content:" ";display:table}.control-sidebar-menu>li>a:after{clear:both}.control-sidebar-menu>li>a>.control-sidebar-subheading{margin-top:0}.control-sidebar-menu .menu-icon{float:left;width:35px;height:35px;border-radius:50%;text-align:center;line-height:35px}.control-sidebar-menu .menu-info{margin-left:45px;margin-top:3px}.control-sidebar-menu .menu-info>.control-sidebar-subheading{margin:0}.control-sidebar-menu .menu-info>p{margin:0;font-size:11px}.control-sidebar-menu .progress{margin:0}.control-sidebar-dark{color:#b8c7ce}.control-sidebar-dark,.control-sidebar-dark+.control-sidebar-bg{background:#222d32}.control-sidebar-dark .nav-tabs.control-sidebar-tabs{border-bottom:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a{background:#181f23;color:#b8c7ce}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#141a1d;border-bottom-color:#141a1d}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:active{background:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover{color:#fff}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#222d32;color:#fff}.control-sidebar-dark .control-sidebar-heading,.control-sidebar-dark .control-sidebar-subheading{color:#fff}.control-sidebar-dark .control-sidebar-menu>li>a:hover{background:#1e282c}.control-sidebar-dark .control-sidebar-menu>li>a .menu-info>p{color:#b8c7ce}.control-sidebar-light{color:#5e5e5e}.control-sidebar-light,.control-sidebar-light+.control-sidebar-bg{background:#f9fafc;border-left:1px solid #d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs{border-bottom:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a{background:#e8ecf4;color:#444}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#d2d6de;border-bottom-color:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:active{background:#eff1f7}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#f9fafc;color:#111}.control-sidebar-light .control-sidebar-heading,.control-sidebar-light .control-sidebar-subheading{color:#111}.control-sidebar-light .control-sidebar-menu{margin-left:-14px}.control-sidebar-light .control-sidebar-menu>li>a:hover{background:#f4f4f5}.control-sidebar-light .control-sidebar-menu>li>a .menu-info>p{color:#5e5e5e}.dropdown-menu{box-shadow:none;border-color:#eee}.dropdown-menu>li>a{color:#777}.dropdown-menu>li>a>.glyphicon,.dropdown-menu>li>a>.fa,.dropdown-menu>li>a>.ion{margin-right:10px}.dropdown-menu>li>a:hover{background-color:#e1e3e9;color:#333}.dropdown-menu>.divider{background-color:#eee}.navbar-nav>.notifications-menu>.dropdown-menu,.navbar-nav>.messages-menu>.dropdown-menu,.navbar-nav>.tasks-menu>.dropdown-menu{width:280px;padding:0 0 0 0;margin:0;top:100%}.navbar-nav>.notifications-menu>.dropdown-menu>li,.navbar-nav>.messages-menu>.dropdown-menu>li,.navbar-nav>.tasks-menu>.dropdown-menu>li{position:relative}.navbar-nav>.notifications-menu>.dropdown-menu>li.header,.navbar-nav>.messages-menu>.dropdown-menu>li.header,.navbar-nav>.tasks-menu>.dropdown-menu>li.header{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;background-color:#ffffff;padding:7px 10px;border-bottom:1px solid #f4f4f4;color:#444444;font-size:14px}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;font-size:12px;background-color:#fff;padding:7px 10px;border-bottom:1px solid #eeeeee;color:#444!important;text-align:center}@media (max-width:991px){.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{background:#fff!important;color:#444!important}}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a:hover{text-decoration:none;font-weight:normal}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu,.navbar-nav>.messages-menu>.dropdown-menu>li .menu,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu{max-height:200px;margin:0;padding:0;list-style:none;overflow-x:hidden}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{display:block;white-space:nowrap;border-bottom:1px solid #f4f4f4}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a:hover{background:#f4f4f4;text-decoration:none}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a{color:#444444;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:10px}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.glyphicon,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.fa,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.ion{width:20px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a{margin:0;padding:10px 10px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>div>img{margin:auto 10px auto auto;width:40px;height:40px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4{padding:0;margin:0 0 0 45px;color:#444444;font-size:15px;position:relative}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4>small{color:#999999;font-size:10px;position:absolute;top:0;right:0}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>p{margin:0 0 0 45px;font-size:12px;color:#888888}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:before,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{content:" ";display:table}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{clear:both}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{padding:10px}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>h3{font-size:14px;padding:0;margin:0 0 10px 0;color:#666666}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>.progress{padding:0;margin:0}.navbar-nav>.user-menu>.dropdown-menu{border-top-right-radius:0;border-top-left-radius:0;padding:1px 0 0 0;border-top-width:0;width:280px}.navbar-nav>.user-menu>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header{height:175px;padding:10px;text-align:center}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img{z-index:5;height:90px;width:90px;border:3px solid;border-color:transparent;border-color:rgba(255,255,255,0.2)}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p{z-index:5;color:#fff;color:rgba(255,255,255,0.8);font-size:17px;margin-top:10px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p>small{display:block;font-size:12px}.navbar-nav>.user-menu>.dropdown-menu>.user-body{padding:15px;border-bottom:1px solid #f4f4f4;border-top:1px solid #dddddd}.navbar-nav>.user-menu>.dropdown-menu>.user-body:before,.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-body a{color:#444 !important}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-body a{background:#fff !important;color:#444 !important}}.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background-color:#f9f9f9;padding:10px}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:before,.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default{color:#666666}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default:hover{background-color:#f9f9f9}}.navbar-nav>.user-menu .user-image{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px;margin-top:-2px}@media (max-width:767px){.navbar-nav>.user-menu .user-image{float:none;margin-right:0;margin-top:-8px;line-height:10px}}.open:not(.dropup)>.animated-dropdown-menu{backface-visibility:visible !important;-webkit-animation:flipInX .7s both;-o-animation:flipInX .7s both;animation:flipInX .7s both}@keyframes flipInX{0%{transform:perspective(400px) rotate3d(1, 0, 0, 90deg);transition-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotate3d(1, 0, 0, -20deg);transition-timing-function:ease-in}60%{transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{transform:perspective(400px)}}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 90deg);-webkit-transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -20deg);-webkit-transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{-webkit-transform:perspective(400px)}}.navbar-custom-menu>.navbar-nav>li{position:relative}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:0;left:auto}@media (max-width:991px){.navbar-custom-menu>.navbar-nav{float:right}.navbar-custom-menu>.navbar-nav>li{position:static}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:5%;left:auto;border:1px solid #ddd;background:#fff}}.form-control{border-radius:0;box-shadow:none;border-color:#d2d6de}.form-control:focus{border-color:#3c8dbc;box-shadow:none}.form-control::-moz-placeholder,.form-control:-ms-input-placeholder,.form-control::-webkit-input-placeholder{color:#bbb;opacity:1}.form-control:not(select){-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-group.has-success label{color:#00a65a}.form-group.has-success .form-control{border-color:#00a65a;box-shadow:none}.form-group.has-warning label{color:#f39c12}.form-group.has-warning .form-control{border-color:#f39c12;box-shadow:none}.form-group.has-error label{color:#dd4b39}.form-group.has-error .form-control{border-color:#dd4b39;box-shadow:none}.input-group .input-group-addon{border-radius:0;border-color:#d2d6de;background-color:#fff}.btn-group-vertical .btn.btn-flat:first-of-type,.btn-group-vertical .btn.btn-flat:last-of-type{border-radius:0}.icheck>label{padding-left:0}.form-control-feedback.fa{line-height:34px}.input-lg+.form-control-feedback.fa,.input-group-lg+.form-control-feedback.fa,.form-group-lg .form-control+.form-control-feedback.fa{line-height:46px}.input-sm+.form-control-feedback.fa,.input-group-sm+.form-control-feedback.fa,.form-group-sm .form-control+.form-control-feedback.fa{line-height:30px}.progress,.progress>.progress-bar{-webkit-box-shadow:none;box-shadow:none}.progress,.progress>.progress-bar,.progress .progress-bar,.progress>.progress-bar .progress-bar{border-radius:1px}.progress.sm,.progress-sm{height:10px}.progress.sm,.progress-sm,.progress.sm .progress-bar,.progress-sm .progress-bar{border-radius:1px}.progress.xs,.progress-xs{height:7px}.progress.xs,.progress-xs,.progress.xs .progress-bar,.progress-xs .progress-bar{border-radius:1px}.progress.xxs,.progress-xxs{height:3px}.progress.xxs,.progress-xxs,.progress.xxs .progress-bar,.progress-xxs .progress-bar{border-radius:1px}.progress.vertical{position:relative;width:30px;height:200px;display:inline-block;margin-right:10px}.progress.vertical>.progress-bar{width:100%;position:absolute;bottom:0}.progress.vertical.sm,.progress.vertical.progress-sm{width:20px}.progress.vertical.xs,.progress.vertical.progress-xs{width:10px}.progress.vertical.xxs,.progress.vertical.progress-xxs{width:3px}.progress-group .progress-text{font-weight:600}.progress-group .progress-number{float:right}.table tr>td .progress{margin:0}.progress-bar-light-blue,.progress-bar-primary{background-color:#3c8dbc}.progress-striped .progress-bar-light-blue,.progress-striped .progress-bar-primary{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-green,.progress-bar-success{background-color:#00a65a}.progress-striped .progress-bar-green,.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-aqua,.progress-bar-info{background-color:#00c0ef}.progress-striped .progress-bar-aqua,.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-yellow,.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-yellow,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-red,.progress-bar-danger{background-color:#dd4b39}.progress-striped .progress-bar-red,.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.small-box{border-radius:2px;position:relative;display:block;margin-bottom:20px;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.small-box>.inner{padding:10px}.small-box>.small-box-footer{position:relative;text-align:center;padding:3px 0;color:#fff;color:rgba(255,255,255,0.8);display:block;z-index:10;background:rgba(0,0,0,0.1);text-decoration:none}.small-box>.small-box-footer:hover{color:#fff;background:rgba(0,0,0,0.15)}.small-box h3{font-size:38px;font-weight:bold;margin:0 0 10px 0;white-space:nowrap;padding:0}.small-box p{font-size:15px}.small-box p>small{display:block;color:#f9f9f9;font-size:13px;margin-top:5px}.small-box h3,.small-box p{z-index:5px}.small-box .icon{-webkit-transition:all .3s linear;-o-transition:all .3s linear;transition:all .3s linear;position:absolute;top:-10px;right:10px;z-index:0;font-size:90px;color:rgba(0,0,0,0.15)}.small-box:hover{text-decoration:none;color:#f9f9f9}.small-box:hover .icon{font-size:95px}@media (max-width:767px){.small-box{text-align:center}.small-box .icon{display:none}.small-box p{font-size:12px}}.box{position:relative;border-radius:3px;background:#ffffff;border-top:3px solid #d2d6de;margin-bottom:20px;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.box.box-primary{border-top-color:#3c8dbc}.box.box-info{border-top-color:#00c0ef}.box.box-danger{border-top-color:#dd4b39}.box.box-warning{border-top-color:#f39c12}.box.box-success{border-top-color:#00a65a}.box.box-default{border-top-color:#d2d6de}.box.collapsed-box .box-body,.box.collapsed-box .box-footer{display:none}.box .nav-stacked>li{border-bottom:1px solid #f4f4f4;margin:0}.box .nav-stacked>li:last-of-type{border-bottom:none}.box.height-control .box-body{max-height:300px;overflow:auto}.box .border-right{border-right:1px solid #f4f4f4}.box .border-left{border-left:1px solid #f4f4f4}.box.box-solid{border-top:0}.box.box-solid>.box-header .btn.btn-default{background:transparent}.box.box-solid>.box-header .btn:hover,.box.box-solid>.box-header a:hover{background:rgba(0,0,0,0.1)}.box.box-solid.box-default{border:1px solid #d2d6de}.box.box-solid.box-default>.box-header{color:#444;background:#d2d6de;background-color:#d2d6de}.box.box-solid.box-default>.box-header a,.box.box-solid.box-default>.box-header .btn{color:#444}.box.box-solid.box-primary{border:1px solid #3c8dbc}.box.box-solid.box-primary>.box-header{color:#fff;background:#3c8dbc;background-color:#3c8dbc}.box.box-solid.box-primary>.box-header a,.box.box-solid.box-primary>.box-header .btn{color:#fff}.box.box-solid.box-info{border:1px solid #00c0ef}.box.box-solid.box-info>.box-header{color:#fff;background:#00c0ef;background-color:#00c0ef}.box.box-solid.box-info>.box-header a,.box.box-solid.box-info>.box-header .btn{color:#fff}.box.box-solid.box-danger{border:1px solid #dd4b39}.box.box-solid.box-danger>.box-header{color:#fff;background:#dd4b39;background-color:#dd4b39}.box.box-solid.box-danger>.box-header a,.box.box-solid.box-danger>.box-header .btn{color:#fff}.box.box-solid.box-warning{border:1px solid #f39c12}.box.box-solid.box-warning>.box-header{color:#fff;background:#f39c12;background-color:#f39c12}.box.box-solid.box-warning>.box-header a,.box.box-solid.box-warning>.box-header .btn{color:#fff}.box.box-solid.box-success{border:1px solid #00a65a}.box.box-solid.box-success>.box-header{color:#fff;background:#00a65a;background-color:#00a65a}.box.box-solid.box-success>.box-header a,.box.box-solid.box-success>.box-header .btn{color:#fff}.box.box-solid>.box-header>.box-tools .btn{border:0;box-shadow:none}.box.box-solid[class*='bg']>.box-header{color:#fff}.box .box-group>.box{margin-bottom:5px}.box .knob-label{text-align:center;color:#333;font-weight:100;font-size:12px;margin-bottom:0.3em}.box>.overlay,.overlay-wrapper>.overlay,.box>.loading-img,.overlay-wrapper>.loading-img{position:absolute;top:0;left:0;width:100%;height:100%}.box .overlay,.overlay-wrapper .overlay{z-index:50;background:rgba(255,255,255,0.7);border-radius:3px}.box .overlay>.fa,.overlay-wrapper .overlay>.fa{position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px;color:#000;font-size:30px}.box .overlay.dark,.overlay-wrapper .overlay.dark{background:rgba(0,0,0,0.5)}.box-header:before,.box-body:before,.box-footer:before,.box-header:after,.box-body:after,.box-footer:after{content:" ";display:table}.box-header:after,.box-body:after,.box-footer:after{clear:both}.box-header{color:#444;display:block;padding:10px;position:relative}.box-header.with-border{border-bottom:1px solid #f4f4f4}.collapsed-box .box-header.with-border{border-bottom:none}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion,.box-header .box-title{display:inline-block;font-size:18px;margin:0;line-height:1}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion{margin-right:5px}.box-header>.box-tools{position:absolute;right:10px;top:5px}.box-header>.box-tools [data-toggle="tooltip"]{position:relative}.box-header>.box-tools.pull-right .dropdown-menu{right:0;left:auto}.btn-box-tool{padding:5px;font-size:12px;background:transparent;color:#97a0b3}.open .btn-box-tool,.btn-box-tool:hover{color:#606c84}.btn-box-tool.btn:active{box-shadow:none}.box-body{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;padding:10px}.no-header .box-body{border-top-right-radius:3px;border-top-left-radius:3px}.box-body>.table{margin-bottom:0}.box-body .fc{margin-top:5px}.box-body .full-width-chart{margin:-19px}.box-body.no-padding .full-width-chart{margin:-9px}.box-body .box-pane{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:3px}.box-body .box-pane-right{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:0}.box-footer{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;border-top:1px solid #f4f4f4;padding:10px;background-color:#fff}.chart-legend{margin:10px 0}@media (max-width:991px){.chart-legend>li{float:left;margin-right:10px}}.box-comments{background:#f7f7f7}.box-comments .box-comment{padding:8px 0;border-bottom:1px solid #eee}.box-comments .box-comment:before,.box-comments .box-comment:after{content:" ";display:table}.box-comments .box-comment:after{clear:both}.box-comments .box-comment:last-of-type{border-bottom:0}.box-comments .box-comment:first-of-type{padding-top:0}.box-comments .box-comment img{float:left}.box-comments .comment-text{margin-left:40px;color:#555}.box-comments .username{color:#444;display:block;font-weight:600}.box-comments .text-muted{font-weight:400;font-size:12px}.todo-list{margin:0;padding:0;list-style:none;overflow:auto}.todo-list>li{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:2px;border-left:2px solid #e6e7e8;color:#444}.todo-list>li:last-of-type{margin-bottom:0}.todo-list>li>input[type='checkbox']{margin:0 10px 0 5px}.todo-list>li .text{display:inline-block;margin-left:5px;font-weight:600}.todo-list>li .label{margin-left:10px;font-size:9px}.todo-list>li .tools{display:none;float:right;color:#dd4b39}.todo-list>li .tools>.fa,.todo-list>li .tools>.glyphicon,.todo-list>li .tools>.ion{margin-right:5px;cursor:pointer}.todo-list>li:hover .tools{display:inline-block}.todo-list>li.done{color:#999}.todo-list>li.done .text{text-decoration:line-through;font-weight:500}.todo-list>li.done .label{background:#d2d6de !important}.todo-list .danger{border-left-color:#dd4b39}.todo-list .warning{border-left-color:#f39c12}.todo-list .info{border-left-color:#00c0ef}.todo-list .success{border-left-color:#00a65a}.todo-list .primary{border-left-color:#3c8dbc}.todo-list .handle{display:inline-block;cursor:move;margin:0 5px}.chat{padding:5px 20px 5px 10px}.chat .item{margin-bottom:10px}.chat .item:before,.chat .item:after{content:" ";display:table}.chat .item:after{clear:both}.chat .item>img{width:40px;height:40px;border:2px solid transparent;border-radius:50%}.chat .item>.online{border:2px solid #00a65a}.chat .item>.offline{border:2px solid #dd4b39}.chat .item>.message{margin-left:55px;margin-top:-40px}.chat .item>.message>.name{display:block;font-weight:600}.chat .item>.attachment{border-radius:3px;background:#f4f4f4;margin-left:65px;margin-right:15px;padding:10px}.chat .item>.attachment>h4{margin:0 0 5px 0;font-weight:600;font-size:14px}.chat .item>.attachment>p,.chat .item>.attachment>.filename{font-weight:600;font-size:13px;font-style:italic;margin:0}.chat .item>.attachment:before,.chat .item>.attachment:after{content:" ";display:table}.chat .item>.attachment:after{clear:both}.box-input{max-width:200px}.modal .panel-body{color:#444}.info-box{display:block;min-height:90px;background:#fff;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:2px;margin-bottom:15px}.info-box small{font-size:14px}.info-box .progress{background:rgba(0,0,0,0.2);margin:5px -10px 5px -10px;height:2px}.info-box .progress,.info-box .progress .progress-bar{border-radius:0}.info-box .progress .progress-bar{background:#fff}.info-box-icon{border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px;display:block;float:left;height:90px;width:90px;text-align:center;font-size:45px;line-height:90px;background:rgba(0,0,0,0.2)}.info-box-icon>img{max-width:100%}.info-box-content{padding:5px 10px;margin-left:90px}.info-box-number{display:block;font-weight:bold;font-size:18px}.progress-description,.info-box-text{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.info-box-text{text-transform:uppercase}.info-box-more{display:block}.progress-description{margin:0}.timeline{position:relative;margin:0 0 30px 0;padding:0;list-style:none}.timeline:before{content:'';position:absolute;top:0;bottom:0;width:4px;background:#ddd;left:31px;margin:0;border-radius:2px}.timeline>li{position:relative;margin-right:10px;margin-bottom:15px}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-item{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;margin-top:0;background:#fff;color:#444;margin-left:60px;margin-right:15px;padding:0;position:relative}.timeline>li>.timeline-item>.time{color:#999;float:right;padding:10px;font-size:12px}.timeline>li>.timeline-item>.timeline-header{margin:0;color:#555;border-bottom:1px solid #f4f4f4;padding:10px;font-size:16px;line-height:1.1}.timeline>li>.timeline-item>.timeline-header>a{font-weight:600}.timeline>li>.timeline-item>.timeline-body,.timeline>li>.timeline-item>.timeline-footer{padding:10px}.timeline>li>.fa,.timeline>li>.glyphicon,.timeline>li>.ion{width:30px;height:30px;font-size:15px;line-height:30px;position:absolute;color:#666;background:#d2d6de;border-radius:50%;text-align:center;left:18px;top:0}.timeline>.time-label>span{font-weight:600;padding:5px;display:inline-block;background-color:#fff;border-radius:4px}.timeline-inverse>li>.timeline-item{background:#f0f0f0;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none}.timeline-inverse>li>.timeline-item>.timeline-header{border-bottom-color:#ddd}.btn{border-radius:3px;-webkit-box-shadow:none;box-shadow:none;border:1px solid transparent}.btn.uppercase{text-transform:uppercase}.btn.btn-flat{border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-width:1px}.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:focus{outline:none}.btn.btn-file{position:relative;overflow:hidden}.btn.btn-file>input[type='file']{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;opacity:0;filter:alpha(opacity=0);outline:none;background:white;cursor:inherit;display:block}.btn-default{background-color:#f4f4f4;color:#444;border-color:#ddd}.btn-default:hover,.btn-default:active,.btn-default.hover{background-color:#e7e7e7}.btn-primary{background-color:#3c8dbc;border-color:#367fa9}.btn-primary:hover,.btn-primary:active,.btn-primary.hover{background-color:#367fa9}.btn-success{background-color:#00a65a;border-color:#008d4c}.btn-success:hover,.btn-success:active,.btn-success.hover{background-color:#008d4c}.btn-info{background-color:#00c0ef;border-color:#00acd6}.btn-info:hover,.btn-info:active,.btn-info.hover{background-color:#00acd6}.btn-danger{background-color:#dd4b39;border-color:#d73925}.btn-danger:hover,.btn-danger:active,.btn-danger.hover{background-color:#d73925}.btn-warning{background-color:#f39c12;border-color:#e08e0b}.btn-warning:hover,.btn-warning:active,.btn-warning.hover{background-color:#e08e0b}.btn-outline{border:1px solid #fff;background:transparent;color:#fff}.btn-outline:hover,.btn-outline:focus,.btn-outline:active{color:rgba(255,255,255,0.7);border-color:rgba(255,255,255,0.7)}.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn[class*='bg-']:hover{-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,0.2);box-shadow:inset 0 0 100px rgba(0,0,0,0.2)}.btn-app{border-radius:3px;position:relative;padding:15px 5px;margin:0 0 10px 10px;min-width:80px;height:60px;text-align:center;color:#666;border:1px solid #ddd;background-color:#f4f4f4;font-size:12px}.btn-app>.fa,.btn-app>.glyphicon,.btn-app>.ion{font-size:20px;display:block}.btn-app:hover{background:#f4f4f4;color:#444;border-color:#aaa}.btn-app:active,.btn-app:focus{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-app>.badge{position:absolute;top:-3px;right:-10px;font-size:10px;font-weight:400}.callout{border-radius:3px;margin:0 0 20px 0;padding:15px 30px 15px 15px;border-left:5px solid #eee}.callout a{color:#fff;text-decoration:underline}.callout a:hover{color:#eee}.callout h4{margin-top:0;font-weight:600}.callout p:last-child{margin-bottom:0}.callout code,.callout .highlight{background-color:#fff}.callout.callout-danger{border-color:#c23321}.callout.callout-warning{border-color:#c87f0a}.callout.callout-info{border-color:#0097bc}.callout.callout-success{border-color:#00733e}.alert{border-radius:3px}.alert h4{font-weight:600}.alert .icon{margin-right:10px}.alert .close{color:#000;opacity:.2;filter:alpha(opacity=20)}.alert .close:hover{opacity:.5;filter:alpha(opacity=50)}.alert a{color:#fff;text-decoration:underline}.alert-success{border-color:#008d4c}.alert-danger,.alert-error{border-color:#d73925}.alert-warning{border-color:#e08e0b}.alert-info{border-color:#00acd6}.nav>li>a:hover,.nav>li>a:active,.nav>li>a:focus{color:#444;background:#f7f7f7}.nav-pills>li>a{border-radius:0;border-top:3px solid transparent;color:#444}.nav-pills>li>a>.fa,.nav-pills>li>a>.glyphicon,.nav-pills>li>a>.ion{margin-right:5px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{border-top-color:#3c8dbc}.nav-pills>li.active>a{font-weight:600}.nav-stacked>li>a{border-radius:0;border-top:0;border-left:3px solid transparent;color:#444}.nav-stacked>li.active>a,.nav-stacked>li.active>a:hover{background:transparent;color:#444;border-top:0;border-left-color:#3c8dbc}.nav-stacked>li.header{border-bottom:1px solid #ddd;color:#777;margin-bottom:10px;padding:5px 10px;text-transform:uppercase}.nav-tabs-custom{margin-bottom:20px;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px}.nav-tabs-custom>.nav-tabs{margin:0;/* border-bottom-color: #3C8DBC /*#f4f4f4*/; */border-top-right-radius:3px;border-top-left-radius:3px}.nav-tabs-custom>.nav-tabs>li{border-top:3px solid transparent;/*margin-bottom:-2px;*/margin-right:5px}.nav-tabs-custom>.nav-tabs>li>a{color:#444;border-radius:0}.nav-tabs-custom>.nav-tabs>li>a.text-muted{color:#999}.nav-tabs-custom>.nav-tabs>li>a,.nav-tabs-custom>.nav-tabs>li>a:hover{background:transparent;margin:0}.nav-tabs-custom>.nav-tabs>li>a:hover{color:#999}.nav-tabs-custom>.nav-tabs>li:not(.active)>a:hover,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:focus,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:active{border-color:transparent}.nav-tabs-custom>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a{background-color:#fff;color:#444}.nav-tabs-custom>.nav-tabs>li.active>a{border-top:1px solid #3C8DBC;border-left:1px solid #3C8DBC;border-right:1px solid #3C8DBC;}.nav-tabs-custom>.nav-tabs>li:first-of-type{margin-left:0}.nav-tabs-custom>.nav-tabs>li:first-of-type.active>a{border-left-color:transparent}.nav-tabs-custom>.nav-tabs.pull-right{float:none!important}.nav-tabs-custom>.nav-tabs.pull-right>li{float:right}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type{margin-right:0}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type>a{border-left-width:1px}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a{border-left-color:#f4f4f4;border-right-color:transparent}.nav-tabs-custom>.nav-tabs>li.header{line-height:35px;padding:0 10px;font-size:20px;color:#444}.nav-tabs-custom>.nav-tabs>li.header>.fa,.nav-tabs-custom>.nav-tabs>li.header>.glyphicon,.nav-tabs-custom>.nav-tabs>li.header>.ion{margin-right:5px}.nav-tabs-custom>.tab-content{background:#fff;padding:10px;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.nav-tabs-custom .dropdown.open>a:active,.nav-tabs-custom .dropdown.open>a:focus{background:transparent;color:#999}.pagination>li>a{background:#fafafa;color:#666}.pagination.pagination-flat>li>a{border-radius:0 !important}.products-list{list-style:none;margin:0;padding:0}.products-list>.item{border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:10px 0;background:#fff}.products-list>.item:before,.products-list>.item:after{content:" ";display:table}.products-list>.item:after{clear:both}.products-list .product-img{float:left}.products-list .product-img img{width:50px;height:50px}.products-list .product-info{margin-left:60px}.products-list .product-title{font-weight:600}.products-list .product-description{display:block;color:#999;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.product-list-in-box>.item{-webkit-box-shadow:none;box-shadow:none;border-radius:0;border-bottom:1px solid #f4f4f4}.product-list-in-box>.item:last-of-type{border-bottom-width:0}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{border-top:1px solid #f4f4f4}.table>thead>tr>th{border-bottom:2px solid #f4f4f4}.table tr td .progress{margin-top:5px}.table-bordered{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table.no-border,.table.no-border td,.table.no-border th{border:0}table.text-center,table.text-center td,table.text-center th{text-align:center}.table.align th{text-align:left}.table.align td{text-align:right}.label-default{background-color:#d2d6de;color:#444}.direct-chat .box-body{border-bottom-right-radius:0;border-bottom-left-radius:0;position:relative;overflow-x:hidden;padding:0}.direct-chat.chat-pane-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-messages{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);padding:10px;height:250px;overflow:auto}.direct-chat-msg,.direct-chat-text{display:block}.direct-chat-msg{margin-bottom:10px}.direct-chat-msg:before,.direct-chat-msg:after{content:" ";display:table}.direct-chat-msg:after{clear:both}.direct-chat-messages,.direct-chat-contacts{-webkit-transition:-webkit-transform .5s ease-in-out;-moz-transition:-moz-transform .5s ease-in-out;-o-transition:-o-transform .5s ease-in-out;transition:transform .5s ease-in-out}.direct-chat-text{border-radius:5px;position:relative;padding:5px 10px;background:#d2d6de;border:1px solid #d2d6de;margin:5px 0 0 50px;color:#444}.direct-chat-text:after,.direct-chat-text:before{position:absolute;right:100%;top:15px;border:solid transparent;border-right-color:#d2d6de;content:' ';height:0;width:0;pointer-events:none}.direct-chat-text:after{border-width:5px;margin-top:-5px}.direct-chat-text:before{border-width:6px;margin-top:-6px}.right .direct-chat-text{margin-right:50px;margin-left:0}.right .direct-chat-text:after,.right .direct-chat-text:before{right:auto;left:100%;border-right-color:transparent;border-left-color:#d2d6de}.direct-chat-img{border-radius:50%;float:left;width:40px;height:40px}.right .direct-chat-img{float:right}.direct-chat-info{display:block;margin-bottom:2px;font-size:12px}.direct-chat-name{font-weight:600}.direct-chat-timestamp{color:#999}.direct-chat-contacts-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-contacts{-webkit-transform:translate(101%, 0);-ms-transform:translate(101%, 0);-o-transform:translate(101%, 0);transform:translate(101%, 0);position:absolute;top:0;bottom:0;height:250px;width:100%;background:#222d32;color:#fff;overflow:auto}.contacts-list>li{border-bottom:1px solid rgba(0,0,0,0.2);padding:10px;margin:0}.contacts-list>li:before,.contacts-list>li:after{content:" ";display:table}.contacts-list>li:after{clear:both}.contacts-list>li:last-of-type{border-bottom:none}.contacts-list-img{border-radius:50%;width:40px;float:left}.contacts-list-info{margin-left:45px;color:#fff}.contacts-list-name,.contacts-list-status{display:block}.contacts-list-name{font-weight:600}.contacts-list-status{font-size:12px}.contacts-list-date{color:#aaa;font-weight:normal}.contacts-list-msg{color:#999}.direct-chat-danger .right>.direct-chat-text{background:#dd4b39;border-color:#dd4b39;color:#fff}.direct-chat-danger .right>.direct-chat-text:after,.direct-chat-danger .right>.direct-chat-text:before{border-left-color:#dd4b39}.direct-chat-primary .right>.direct-chat-text{background:#3c8dbc;border-color:#3c8dbc;color:#fff}.direct-chat-primary .right>.direct-chat-text:after,.direct-chat-primary .right>.direct-chat-text:before{border-left-color:#3c8dbc}.direct-chat-warning .right>.direct-chat-text{background:#f39c12;border-color:#f39c12;color:#fff}.direct-chat-warning .right>.direct-chat-text:after,.direct-chat-warning .right>.direct-chat-text:before{border-left-color:#f39c12}.direct-chat-info .right>.direct-chat-text{background:#00c0ef;border-color:#00c0ef;color:#fff}.direct-chat-info .right>.direct-chat-text:after,.direct-chat-info .right>.direct-chat-text:before{border-left-color:#00c0ef}.direct-chat-success .right>.direct-chat-text{background:#00a65a;border-color:#00a65a;color:#fff}.direct-chat-success .right>.direct-chat-text:after,.direct-chat-success .right>.direct-chat-text:before{border-left-color:#00a65a}.users-list>li{width:25%;float:left;padding:10px;text-align:center}.users-list>li img{border-radius:50%;max-width:100%;height:auto}.users-list>li>a:hover,.users-list>li>a:hover .users-list-name{color:#999}.users-list-name,.users-list-date{display:block}.users-list-name{font-weight:600;color:#444;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.users-list-date{color:#999;font-size:12px}.carousel-control.left,.carousel-control.right{background-image:none}.carousel-control>.fa{font-size:40px;position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-20px}.modal{background:rgba(0,0,0,0.3)}.modal-content{border-radius:0;-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125);border:0}@media (min-width:768px){.modal-content{-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125)}}.modal-header{border-bottom-color:#f4f4f4}.modal-footer{border-top-color:#f4f4f4}.modal-primary .modal-header,.modal-primary .modal-footer{border-color:#307095}.modal-warning .modal-header,.modal-warning .modal-footer{border-color:#c87f0a}.modal-info .modal-header,.modal-info .modal-footer{border-color:#0097bc}.modal-success .modal-header,.modal-success .modal-footer{border-color:#00733e}.modal-danger .modal-header,.modal-danger .modal-footer{border-color:#c23321}.box-widget{border:none;position:relative}.widget-user .widget-user-header{padding:20px;height:120px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user .widget-user-username{margin-top:0;margin-bottom:5px;font-size:25px;font-weight:300;text-shadow:0 1px 1px rgba(0,0,0,0.2)}.widget-user .widget-user-desc{margin-top:0}.widget-user .widget-user-image{position:absolute;top:65px;left:50%;margin-left:-45px}.widget-user .widget-user-image>img{width:90px;height:auto;border:3px solid #fff}.widget-user .box-footer{padding-top:30px}.widget-user-2 .widget-user-header{padding:20px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user-2 .widget-user-username{margin-top:5px;margin-bottom:5px;font-size:25px;font-weight:300}.widget-user-2 .widget-user-desc{margin-top:0}.widget-user-2 .widget-user-username,.widget-user-2 .widget-user-desc{margin-left:75px}.widget-user-2 .widget-user-image>img{width:65px;height:auto;float:left}.mailbox-messages>.table{margin:0}.mailbox-controls{padding:5px}.mailbox-controls.with-border{border-bottom:1px solid #f4f4f4}.mailbox-read-info{border-bottom:1px solid #f4f4f4;padding:10px}.mailbox-read-info h3{font-size:20px;margin:0}.mailbox-read-info h5{margin:0;padding:5px 0 0 0}.mailbox-read-time{color:#999;font-size:13px}.mailbox-read-message{padding:10px}.mailbox-attachments li{float:left;width:200px;border:1px solid #eee;margin-bottom:10px;margin-right:10px}.mailbox-attachment-name{font-weight:bold;color:#666}.mailbox-attachment-icon,.mailbox-attachment-info,.mailbox-attachment-size{display:block}.mailbox-attachment-info{padding:10px;background:#f4f4f4}.mailbox-attachment-size{color:#999;font-size:12px}.mailbox-attachment-icon{text-align:center;font-size:65px;color:#666;padding:20px 10px}.mailbox-attachment-icon.has-img{padding:0}.mailbox-attachment-icon.has-img>img{max-width:100%;height:auto}.lockscreen{background:#d2d6de}.lockscreen-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.lockscreen-logo a{color:#444}.lockscreen-wrapper{max-width:400px;margin:0 auto;margin-top:10%}.lockscreen .lockscreen-name{text-align:center;font-weight:600}.lockscreen-item{border-radius:4px;padding:0;background:#fff;position:relative;margin:10px auto 30px auto;width:290px}.lockscreen-image{border-radius:50%;position:absolute;left:-10px;top:-25px;background:#fff;padding:5px;z-index:10}.lockscreen-image>img{border-radius:50%;width:70px;height:70px}.lockscreen-credentials{margin-left:70px}.lockscreen-credentials .form-control{border:0}.lockscreen-credentials .btn{background-color:#fff;border:0;padding:0 10px}.lockscreen-footer{margin-top:10px}.login-logo,.register-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.login-logo a,.register-logo a{color:#444}.login-page,.register-page{background:#d2d6de}.login-box,.register-box{width:360px;margin:7% auto}@media (max-width:768px){.login-box,.register-box{width:90%;margin-top:20px}}.login-box-body,.register-box-body{background:#fff;padding:20px;border-top:0;color:#666}.login-box-body .form-control-feedback,.register-box-body .form-control-feedback{color:#777}.login-box-msg,.register-box-msg{margin:0;text-align:center;padding:0 20px 20px 20px}.social-auth-links{margin:10px 0}.error-page{width:600px;margin:20px auto 0 auto}@media (max-width:991px){.error-page{width:100%}}.error-page>.headline{float:left;font-size:100px;font-weight:300}@media (max-width:991px){.error-page>.headline{float:none;text-align:center}}.error-page>.error-content{margin-left:190px;display:block}@media (max-width:991px){.error-page>.error-content{margin-left:0}}.error-page>.error-content>h3{font-weight:300;font-size:25px}@media (max-width:991px){.error-page>.error-content>h3{text-align:center}}.invoice{position:relative;background:#fff;border:1px solid #f4f4f4;padding:20px;margin:10px 25px}.invoice-title{margin-top:0}.profile-user-img{margin:0 auto;width:100px;padding:3px;border:3px solid #d2d6de}.profile-username{font-size:21px;margin-top:5px}.post{border-bottom:1px solid #d2d6de;margin-bottom:15px;padding-bottom:15px;color:#666}.post:last-of-type{border-bottom:0;margin-bottom:0;padding-bottom:0}.post .user-block{margin-bottom:15px}.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon>:first-child{border:none;text-align:center;width:100%}.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0}.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0}.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0}.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn.focus,.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}.btn-adn .badge{color:#d87a68;background-color:#fff}.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket.focus,.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}.btn-bitbucket .badge{color:#205081;background-color:#fff}.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox.focus,.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}.btn-dropbox .badge{color:#1087dd;background-color:#fff}.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook.focus,.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}.btn-facebook .badge{color:#3b5998;background-color:#fff}.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr.focus,.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}.btn-flickr .badge{color:#ff0084;background-color:#fff}.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare.focus,.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}.btn-foursquare .badge{color:#f94877;background-color:#fff}.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:hover,.btn-github:focus,.btn-github.focus,.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}.btn-github .badge{color:#444;background-color:#fff}.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:hover,.btn-google:focus,.btn-google.focus,.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none}.btn-google .badge{color:#dd4b39;background-color:#fff}.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram.focus,.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}.btn-instagram .badge{color:#3f729b;background-color:#fff}.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin.focus,.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}.btn-linkedin .badge{color:#007bb6;background-color:#fff}.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft.focus,.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}.btn-microsoft .badge{color:#2672ec;background-color:#fff}.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid.focus,.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}.btn-openid .badge{color:#f7931e;background-color:#fff}.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:hover,.btn-pinterest:focus,.btn-pinterest.focus,.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}.btn-pinterest .badge{color:#cb2027;background-color:#fff}.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit.focus,.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}.btn-reddit .badge{color:#eff7ff;background-color:#000}.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud.focus,.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}.btn-soundcloud .badge{color:#f50;background-color:#fff}.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr.focus,.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}.btn-tumblr .badge{color:#2c4762;background-color:#fff}.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter.focus,.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}.btn-twitter .badge{color:#55acee;background-color:#fff}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo.focus,.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}.btn-vimeo .badge{color:#1ab7ea;background-color:#fff}.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk.focus,.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}.btn-vk .badge{color:#587ea3;background-color:#fff}.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo.focus,.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}.btn-yahoo .badge{color:#720e9e;background-color:#fff}.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none;border:1px solid #3c8dbc}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}.pad{padding:10px}.margin{margin:10px}.margin-bottom{margin-bottom:20px}.margin-bottom-none{margin-bottom:0}.margin-r-5{margin-right:5px}.inline{display:inline}.description-block{display:block;margin:10px 0;text-align:center}.description-block.margin-bottom{margin-bottom:25px}.description-block>.description-header{margin:0;padding:0;font-weight:600;font-size:16px}.description-block>.description-text{text-transform:uppercase}.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.alert-success,.alert-danger,.alert-error,.alert-warning,.alert-info,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer{color:#fff !important}.bg-gray{color:#000;background-color:#d2d6de !important}.bg-gray-light{background-color:#f7f7f7}.bg-black{background-color:#111 !important}.bg-red,.callout.callout-danger,.alert-danger,.alert-error,.label-danger,.modal-danger .modal-body{background-color:#dd4b39 !important}.bg-yellow,.callout.callout-warning,.alert-warning,.label-warning,.modal-warning .modal-body{background-color:#f39c12 !important}.bg-aqua,.callout.callout-info,.alert-info,.label-info,.modal-info .modal-body{background-color:#00c0ef !important}.bg-blue{background-color:#0073b7 !important}.bg-light-blue,.label-primary,.modal-primary .modal-body{background-color:#3c8dbc !important}.bg-green,.callout.callout-success,.alert-success,.label-success,.modal-success .modal-body{background-color:#00a65a !important}.bg-navy{background-color:#001f3f !important}.bg-teal{background-color:#39cccc !important}.bg-olive{background-color:#3d9970 !important}.bg-lime{background-color:#01ff70 !important}.bg-orange{background-color:#ff851b !important}.bg-fuchsia{background-color:#f012be !important}.bg-purple{background-color:#605ca8 !important}.bg-maroon{background-color:#d81b60 !important}.bg-gray-active{color:#000;background-color:#b5bbc8 !important}.bg-black-active{background-color:#000 !important}.bg-red-active,.modal-danger .modal-header,.modal-danger .modal-footer{background-color:#d33724 !important}.bg-yellow-active,.modal-warning .modal-header,.modal-warning .modal-footer{background-color:#db8b0b !important}.bg-aqua-active,.modal-info .modal-header,.modal-info .modal-footer{background-color:#00a7d0 !important}.bg-blue-active{background-color:#005384 !important}.bg-light-blue-active,.modal-primary .modal-header,.modal-primary .modal-footer{background-color:#357ca5 !important}.bg-green-active,.modal-success .modal-header,.modal-success .modal-footer{background-color:#008d4c !important}.bg-navy-active{background-color:#001a35 !important}.bg-teal-active{background-color:#30bbbb !important}.bg-olive-active{background-color:#368763 !important}.bg-lime-active{background-color:#00e765 !important}.bg-orange-active{background-color:#ff7701 !important}.bg-fuchsia-active{background-color:#db0ead !important}.bg-purple-active{background-color:#555299 !important}.bg-maroon-active{background-color:#ca195a !important}[class^="bg-"].disabled{opacity:.65;filter:alpha(opacity=65)}.text-red{color:#dd4b39 !important}.text-yellow{color:#f39c12 !important}.text-aqua{color:#00c0ef !important}.text-blue{color:#0073b7 !important}.text-black{color:#111 !important}.text-light-blue{color:#3c8dbc !important}.text-green{color:#00a65a !important}.text-gray{color:#d2d6de !important}.text-navy{color:#001f3f !important}.text-teal{color:#39cccc !important}.text-olive{color:#3d9970 !important}.text-lime{color:#01ff70 !important}.text-orange{color:#ff851b !important}.text-fuchsia{color:#f012be !important}.text-purple{color:#605ca8 !important}.text-maroon{color:#d81b60 !important}.link-muted{color:#7a869d}.link-muted:hover,.link-muted:focus{color:#606c84}.link-black{color:#666}.link-black:hover,.link-black:focus{color:#999}.hide{display:none !important}.no-border{border:0 !important}.no-padding{padding:0 !important}.no-margin{margin:0 !important}.no-shadow{box-shadow:none!important}.list-unstyled,.chart-legend,.contacts-list,.users-list,.mailbox-attachments{list-style:none;margin:0;padding:0}.list-group-unbordered>.list-group-item{border-left:0;border-right:0;border-radius:0;padding-left:0;padding-right:0}.flat{border-radius:0 !important}.text-bold,.text-bold.table td,.text-bold.table th{font-weight:700}.text-sm{font-size:12px}.jqstooltip{padding:5px!important;width:auto!important;height:auto!important}.bg-teal-gradient{background:#39cccc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important;background:-ms-linear-gradient(bottom, #39cccc, #7adddd) !important;background:-moz-linear-gradient(center bottom, #39cccc 0, #7adddd 100%) !important;background:-o-linear-gradient(#7adddd, #39cccc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important;color:#fff}.bg-light-blue-gradient{background:#3c8dbc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;background:-ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;background:-moz-linear-gradient(center bottom, #3c8dbc 0, #67a8ce 100%) !important;background:-o-linear-gradient(#67a8ce, #3c8dbc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;color:#fff}.bg-blue-gradient{background:#0073b7 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;background:-ms-linear-gradient(bottom, #0073b7, #0089db) !important;background:-moz-linear-gradient(center bottom, #0073b7 0, #0089db 100%) !important;background:-o-linear-gradient(#0089db, #0073b7) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;color:#fff}.bg-aqua-gradient{background:#00c0ef !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;background:-ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;background:-moz-linear-gradient(center bottom, #00c0ef 0, #14d1ff 100%) !important;background:-o-linear-gradient(#14d1ff, #00c0ef) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;color:#fff}.bg-yellow-gradient{background:#f39c12 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;background:-ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;background:-moz-linear-gradient(center bottom, #f39c12 0, #f7bc60 100%) !important;background:-o-linear-gradient(#f7bc60, #f39c12) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;color:#fff}.bg-purple-gradient{background:#605ca8 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important;background:-ms-linear-gradient(bottom, #605ca8, #9491c4) !important;background:-moz-linear-gradient(center bottom, #605ca8 0, #9491c4 100%) !important;background:-o-linear-gradient(#9491c4, #605ca8) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important;color:#fff}.bg-green-gradient{background:#00a65a !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;background:-ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;background:-moz-linear-gradient(center bottom, #00a65a 0, #00ca6d 100%) !important;background:-o-linear-gradient(#00ca6d, #00a65a) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;color:#fff}.bg-red-gradient{background:#dd4b39 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important;background:-ms-linear-gradient(bottom, #dd4b39, #e47365) !important;background:-moz-linear-gradient(center bottom, #dd4b39 0, #e47365 100%) !important;background:-o-linear-gradient(#e47365, #dd4b39) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important;color:#fff}.bg-black-gradient{background:#111 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #111), color-stop(1, #2b2b2b)) !important;background:-ms-linear-gradient(bottom, #111, #2b2b2b) !important;background:-moz-linear-gradient(center bottom, #111 0, #2b2b2b 100%) !important;background:-o-linear-gradient(#2b2b2b, #111) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111111', GradientType=0) !important;color:#fff}.bg-maroon-gradient{background:#d81b60 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #d81b60), color-stop(1, #e73f7c)) !important;background:-ms-linear-gradient(bottom, #d81b60, #e73f7c) !important;background:-moz-linear-gradient(center bottom, #d81b60 0, #e73f7c 100%) !important;background:-o-linear-gradient(#e73f7c, #d81b60) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#d81b60', GradientType=0) !important;color:#fff}.description-block .description-icon{font-size:16px}.no-pad-top{padding-top:0}.position-static{position:static!important}.list-header{font-size:15px;padding:10px 4px;font-weight:bold;color:#666}.list-seperator{height:1px;background:#f4f4f4;margin:15px 0 9px 0}.list-link>a{padding:4px;color:#777}.list-link>a:hover{color:#222}.font-light{font-weight:300}.user-block:before,.user-block:after{content:" ";display:table}.user-block:after{clear:both}.user-block img{width:40px;height:40px;float:left}.user-block .username,.user-block .description,.user-block .comment{display:block;margin-left:50px}.user-block .username{font-size:16px;font-weight:600}.user-block .description{color:#999;font-size:13px}.user-block.user-block-sm .username,.user-block.user-block-sm .description,.user-block.user-block-sm .comment{margin-left:40px}.user-block.user-block-sm .username{font-size:14px}.img-sm,.img-md,.img-lg,.box-comments .box-comment img,.user-block.user-block-sm img{float:left}.img-sm,.box-comments .box-comment img,.user-block.user-block-sm img{width:30px!important;height:30px!important}.img-sm+.img-push{margin-left:40px}.img-md{width:60px;height:60px}.img-md+.img-push{margin-left:70px}.img-lg{width:100px;height:100px}.img-lg+.img-push{margin-left:110px}.img-bordered{border:3px solid #d2d6de;padding:3px}.img-bordered-sm{border:2px solid #d2d6de;padding:2px}.attachment-block{border:1px solid #f4f4f4;padding:5px;margin-bottom:10px;background:#f7f7f7}.attachment-block .attachment-img{max-width:100px;max-height:100px;height:auto;float:left}.attachment-block .attachment-pushed{margin-left:110px}.attachment-block .attachment-heading{margin:0}.attachment-block .attachment-text{color:#555}.connectedSortable{min-height:100px}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sort-highlight{background:#f4f4f4;border:1px dashed #ddd;margin-bottom:10px}.full-opacity-hover{opacity:.65;filter:alpha(opacity=65)}.full-opacity-hover:hover{opacity:1;filter:alpha(opacity=100)}.chart{position:relative;overflow:hidden;width:100%}.chart svg,.chart canvas{width:100%!important}@media print{.no-print,.main-sidebar,.left-side,.main-header,.content-header{display:none!important}.content-wrapper,.right-side,.main-footer{margin-left:0!important;min-height:0!important;-webkit-transform:translate(0, 0) !important;-ms-transform:translate(0, 0) !important;-o-transform:translate(0, 0) !important;transform:translate(0, 0) !important}.fixed .content-wrapper,.fixed .right-side{padding-top:0!important}.invoice{width:100%;border:0;margin:0;padding:0}.invoice-col{float:left;width:33.3333333%}.table-responsive{overflow:auto}.table-responsive>.table tr th,.table-responsive>.table tr td{white-space:normal!important}}
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css b/LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css
new file mode 100644 (file)
index 0000000..123c04f
--- /dev/null
@@ -0,0 +1 @@
+.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/AdminLTE/js/app.min.js b/LinuxGUI/WebUI/static/AdminLTE/js/app.min.js
new file mode 100644 (file)
index 0000000..679f18f
--- /dev/null
@@ -0,0 +1,13 @@
+/*! AdminLTE app.js
+ * ================
+ * Main JS application file for AdminLTE v2. This file
+ * should be included in all pages. It controls some layout
+ * options and implements exclusive AdminLTE plugins.
+ *
+ * @Author  Almsaeed Studio
+ * @Support <http://www.almsaeedstudio.com>
+ * @Email   <support@almsaeedstudio.com>
+ * @version 2.3.0
+ * @license MIT <http://opensource.org/licenses/MIT>
+ */
+function _init(){"use strict";$.AdminLTE.layout={activate:function(){var a=this;a.fix(),a.fixSidebar(),$(window,".wrapper").resize(function(){a.fix(),a.fixSidebar()})},fix:function(){var a=$(".main-header").outerHeight()+$(".main-footer").outerHeight(),b=$(window).height(),c=$(".sidebar").height();if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",b-$(".main-footer").outerHeight());else{var d;b>=c?($(".content-wrapper, .right-side").css("min-height",b-a),d=b-a):($(".content-wrapper, .right-side").css("min-height",c),d=c);var e=$($.AdminLTE.options.controlSidebarOptions.selector);"undefined"!=typeof e&&e.height()>d&&$(".content-wrapper, .right-side").css("min-height",e.height())}},fixSidebar:function(){return $("body").hasClass("fixed")?("undefined"==typeof $.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),void($.AdminLTE.options.sidebarSlimScroll&&"undefined"!=typeof $.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimscroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"})))):void("undefined"!=typeof $.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"))}},$.AdminLTE.pushMenu={activate:function(a){var b=$.AdminLTE.options.screenSizes;$(a).on("click",function(a){a.preventDefault(),$(window).width()>b.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=b.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var a=this,b=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>b&&a.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>b&&a.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(a){var b=this,c=$.AdminLTE.options.animationSpeed;$(document).on("click",a+" li a",function(a){var d=$(this),e=d.next();if(e.is(".treeview-menu")&&e.is(":visible"))e.slideUp(c,function(){e.removeClass("menu-open")}),e.parent("li").removeClass("active");else if(e.is(".treeview-menu")&&!e.is(":visible")){var f=d.parents("ul").first(),g=f.find("ul:visible").slideUp(c);g.removeClass("menu-open");var h=d.parent("li");e.slideDown(c,function(){e.addClass("menu-open"),f.find("li.active").removeClass("active"),h.addClass("active"),b.layout.fix()})}e.is(".treeview-menu")&&a.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var a=this,b=$.AdminLTE.options.controlSidebarOptions,c=$(b.selector),d=$(b.toggleBtnSelector);d.on("click",function(d){d.preventDefault(),c.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?a.close(c,b.slide):a.open(c,b.slide)});var e=$(".control-sidebar-bg");a._fix(e),$("body").hasClass("fixed")?a._fixForFixed(c):$(".content-wrapper, .right-side").height()<c.height()&&a._fixForContent(c)},open:function(a,b){b?a.addClass("control-sidebar-open"):$("body").addClass("control-sidebar-open")},close:function(a,b){b?a.removeClass("control-sidebar-open"):$("body").removeClass("control-sidebar-open")},_fix:function(a){var b=this;$("body").hasClass("layout-boxed")?(a.css("position","absolute"),a.height($(".wrapper").height()),$(window).resize(function(){b._fix(a)})):a.css({position:"fixed",height:"auto"})},_fixForFixed:function(a){a.css({position:"fixed","max-height":"100%",overflow:"auto","padding-bottom":"50px"})},_fixForContent:function(a){$(".content-wrapper, .right-side").css("min-height",a.height())}},$.AdminLTE.boxWidget={selectors:$.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors,icons:$.AdminLTE.options.boxWidgetOptions.boxWidgetIcons,animationSpeed:$.AdminLTE.options.animationSpeed,activate:function(a){var b=this;a||(a=document),$(a).on("click",b.selectors.collapse,function(a){a.preventDefault(),b.collapse($(this))}),$(a).on("click",b.selectors.remove,function(a){a.preventDefault(),b.remove($(this))})},collapse:function(a){var b=this,c=a.parents(".box").first(),d=c.find("> .box-body, > .box-footer, > form  >.box-body, > form > .box-footer");c.hasClass("collapsed-box")?(a.children(":first").removeClass(b.icons.open).addClass(b.icons.collapse),d.slideDown(b.animationSpeed,function(){c.removeClass("collapsed-box")})):(a.children(":first").removeClass(b.icons.collapse).addClass(b.icons.open),d.slideUp(b.animationSpeed,function(){c.addClass("collapsed-box")}))},remove:function(a){var b=a.parents(".box").first();b.slideUp(this.animationSpeed)}}}if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");$.AdminLTE={},$.AdminLTE.options={navbarMenuSlimscroll:!0,navbarMenuSlimscrollWidth:"3px",navbarMenuHeight:"200px",animationSpeed:500,sidebarToggleSelector:"[data-toggle='offcanvas']",sidebarPushMenu:!0,sidebarSlimScroll:!0,sidebarExpandOnHover:!1,enableBoxRefresh:!0,enableBSToppltip:!0,BSTooltipSelector:"[data-toggle='tooltip']",enableFastclick:!0,enableControlSidebar:!0,controlSidebarOptions:{toggleBtnSelector:"[data-toggle='control-sidebar']",selector:".control-sidebar",slide:!0},enableBoxWidget:!0,boxWidgetOptions:{boxWidgetIcons:{collapse:"fa-minus",open:"fa-plus",remove:"fa-times"},boxWidgetSelectors:{remove:'[data-widget="remove"]',collapse:'[data-widget="collapse"]'}},directChat:{enable:!0,contactToggleSelector:'[data-widget="chat-pane-toggle"]'},colors:{lightBlue:"#3c8dbc",red:"#f56954",green:"#00a65a",aqua:"#00c0ef",yellow:"#f39c12",blue:"#0073b7",navy:"#001F3F",teal:"#39CCCC",olive:"#3D9970",lime:"#01FF70",orange:"#FF851B",fuchsia:"#F012BE",purple:"#8E24AA",maroon:"#D81B60",black:"#222222",gray:"#d2d6de"},screenSizes:{xs:480,sm:768,md:992,lg:1200}},$(function(){"use strict";$("body").removeClass("hold-transition"),"undefined"!=typeof AdminLTEOptions&&$.extend(!0,$.AdminLTE.options,AdminLTEOptions);var a=$.AdminLTE.options;_init(),$.AdminLTE.layout.activate(),$.AdminLTE.tree(".sidebar"),a.enableControlSidebar&&$.AdminLTE.controlSidebar.activate(),a.navbarMenuSlimscroll&&"undefined"!=typeof $.fn.slimscroll&&$(".navbar .menu").slimscroll({height:a.navbarMenuHeight,alwaysVisible:!1,size:a.navbarMenuSlimscrollWidth}).css("width","100%"),a.sidebarPushMenu&&$.AdminLTE.pushMenu.activate(a.sidebarToggleSelector),a.enableBSToppltip&&$("body").tooltip({selector:a.BSTooltipSelector}),a.enableBoxWidget&&$.AdminLTE.boxWidget.activate(),a.enableFastclick&&"undefined"!=typeof FastClick&&FastClick.attach(document.body),a.directChat.enable&&$(document).on("click",a.directChat.contactToggleSelector,function(){var a=$(this).parents(".direct-chat").first();a.toggleClass("direct-chat-contacts-open")}),$('.btn-group[data-toggle="btn-toggle"]').each(function(){var a=$(this);$(this).find(".btn").on("click",function(b){a.find(".btn.active").removeClass("active"),$(this).addClass("active"),b.preventDefault()})})}),function(a){"use strict";a.fn.boxRefresh=function(b){function c(a){a.append(f),e.onLoadStart.call(a)}function d(a){a.find(f).remove(),e.onLoadDone.call(a)}var e=a.extend({trigger:".refresh-btn",source:"",onLoadStart:function(a){return a},onLoadDone:function(a){return a}},b),f=a('<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>');return this.each(function(){if(""===e.source)return void(window.console&&window.console.log("Please specify a source first - boxRefresh()"));var b=a(this),f=b.find(e.trigger).first();f.on("click",function(a){a.preventDefault(),c(b),b.find(".box-body").load(e.source,function(){d(b)})})})}}(jQuery),function(a){"use strict";a.fn.activateBox=function(){a.AdminLTE.boxWidget.activate(this)}}(jQuery),function(a){"use strict";a.fn.todolist=function(b){var c=a.extend({onCheck:function(a){return a},onUncheck:function(a){return a}},b);return this.each(function(){"undefined"!=typeof a.fn.iCheck?(a("input",this).on("ifChecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onCheck.call(b)}),a("input",this).on("ifUnchecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onUncheck.call(b)})):a("input",this).on("change",function(){var b=a(this).parents("li").first();b.toggleClass("done"),a("input",b).is(":checked")?c.onCheck.call(b):c.onUncheck.call(b)})})}}(jQuery);
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css
new file mode 100644 (file)
index 0000000..61358b1
--- /dev/null
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css
new file mode 100644 (file)
index 0000000..be11b9f
--- /dev/null
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px;font-size:14px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot
new file mode 100644 (file)
index 0000000..b93a495
Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot differ
diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg
new file mode 100644 (file)
index 0000000..94fb549
--- /dev/null
@@ -0,0 +1,288 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf
new file mode 100644 (file)
index 0000000..1413fc6
Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff
new file mode 100644 (file)
index 0000000..9e61285
Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff differ
diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2
new file mode 100644 (file)
index 0000000..64539b5
Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js b/LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js
new file mode 100644 (file)
index 0000000..133aeec
--- /dev/null
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),
+d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/css/font-awesome.min.css b/LinuxGUI/WebUI/static/css/font-awesome.min.css
new file mode 100644 (file)
index 0000000..aae88c4
--- /dev/null
@@ -0,0 +1,4 @@
+/*!\r
+ *  Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome\r
+ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\r
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}\r
diff --git a/LinuxGUI/WebUI/static/css/ionicons.min.css b/LinuxGUI/WebUI/static/css/ionicons.min.css
new file mode 100644 (file)
index 0000000..e4bd1ef
--- /dev/null
@@ -0,0 +1,11 @@
+@charset "UTF-8";/*!\r
+  Ionicons, v2.0.1\r
+  Created by Ben Sperry for the Ionic Framework, http://ionicons.com/\r
+  https://twitter.com/benjsperry  https://twitter.com/ionicframework\r
+  MIT License: https://github.com/driftyco/ionicons\r
+\r
+  Android-style icons originally built by Google’s\r
+  Material Design Icons: https://github.com/google/material-design-icons\r
+  used under CC BY http://creativecommons.org/licenses/by/4.0/\r
+  Modified icons to fit ionicon’s grid from original.\r
+*/@font-face{font-family:"Ionicons";src:url("../fonts/ionicons.eot?v=2.0.1");src:url("../fonts/ionicons.eot?v=2.0.1#iefix") format("embedded-opentype"),url("../fonts/ionicons.ttf?v=2.0.1") format("truetype"),url("../fonts/ionicons.woff?v=2.0.1") format("woff"),url("../fonts/ionicons.svg?v=2.0.1#Ionicons") format("svg");font-weight:normal;font-style:normal}.ion,.ionicons,.ion-alert:before,.ion-alert-circled:before,.ion-android-add:before,.ion-android-add-circle:before,.ion-android-alarm-clock:before,.ion-android-alert:before,.ion-android-apps:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down:before,.ion-android-arrow-dropdown:before,.ion-android-arrow-dropdown-circle:before,.ion-android-arrow-dropleft:before,.ion-android-arrow-dropleft-circle:before,.ion-android-arrow-dropright:before,.ion-android-arrow-dropright-circle:before,.ion-android-arrow-dropup:before,.ion-android-arrow-dropup-circle:before,.ion-android-arrow-forward:before,.ion-android-arrow-up:before,.ion-android-attach:before,.ion-android-bar:before,.ion-android-bicycle:before,.ion-android-boat:before,.ion-android-bookmark:before,.ion-android-bulb:before,.ion-android-bus:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-cancel:before,.ion-android-car:before,.ion-android-cart:before,.ion-android-chat:before,.ion-android-checkbox:before,.ion-android-checkbox-blank:before,.ion-android-checkbox-outline:before,.ion-android-checkbox-outline-blank:before,.ion-android-checkmark-circle:before,.ion-android-clipboard:before,.ion-android-close:before,.ion-android-cloud:before,.ion-android-cloud-circle:before,.ion-android-cloud-done:before,.ion-android-cloud-outline:before,.ion-android-color-palette:before,.ion-android-compass:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-contract:before,.ion-android-create:before,.ion-android-delete:before,.ion-android-desktop:before,.ion-android-document:before,.ion-android-done:before,.ion-android-done-all:before,.ion-android-download:before,.ion-android-drafts:before,.ion-android-exit:before,.ion-android-expand:before,.ion-android-favorite:before,.ion-android-favorite-outline:before,.ion-android-film:before,.ion-android-folder:before,.ion-android-folder-open:before,.ion-android-funnel:before,.ion-android-globe:before,.ion-android-hand:before,.ion-android-hangout:before,.ion-android-happy:before,.ion-android-home:before,.ion-android-image:before,.ion-android-laptop:before,.ion-android-list:before,.ion-android-locate:before,.ion-android-lock:before,.ion-android-mail:before,.ion-android-map:before,.ion-android-menu:before,.ion-android-microphone:before,.ion-android-microphone-off:before,.ion-android-more-horizontal:before,.ion-android-more-vertical:before,.ion-android-navigate:before,.ion-android-notifications:before,.ion-android-notifications-none:before,.ion-android-notifications-off:before,.ion-android-open:before,.ion-android-options:before,.ion-android-people:before,.ion-android-person:before,.ion-android-person-add:before,.ion-android-phone-landscape:before,.ion-android-phone-portrait:before,.ion-android-pin:before,.ion-android-plane:before,.ion-android-playstore:before,.ion-android-print:before,.ion-android-radio-button-off:before,.ion-android-radio-button-on:before,.ion-android-refresh:before,.ion-android-remove:before,.ion-android-remove-circle:before,.ion-android-restaurant:before,.ion-android-sad:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share:before,.ion-android-share-alt:before,.ion-android-star:before,.ion-android-star-half:before,.ion-android-star-outline:before,.ion-android-stopwatch:before,.ion-android-subway:before,.ion-android-sunny:before,.ion-android-sync:before,.ion-android-textsms:before,.ion-android-time:before,.ion-android-train:before,.ion-android-unlock:before,.ion-android-upload:before,.ion-android-volume-down:before,.ion-android-volume-mute:before,.ion-android-volume-off:before,.ion-android-volume-up:before,.ion-android-walk:before,.ion-android-warning:before,.ion-android-watch:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-backspace:before,.ion-backspace-outline:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-bowtie:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox:before,.ion-chatbox-working:before,.ion-chatboxes:before,.ion-chatbubble:before,.ion-chatbubble-working:before,.ion-chatbubbles:before,.ion-checkmark:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close:before,.ion-close-circled:before,.ion-close-round:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code:before,.ion-code-download:before,.ion-code-working:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-crop:before,.ion-cube:before,.ion-disc:before,.ion-document:before,.ion-document-text:before,.ion-drag:before,.ion-earth:before,.ion-easel:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email:before,.ion-email-unread:before,.ion-erlenmeyer-flask:before,.ion-erlenmeyer-flask-bubbles:before,.ion-eye:before,.ion-eye-disabled:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash:before,.ion-flash-off:before,.ion-folder:before,.ion-fork:before,.ion-fork-repo:before,.ion-forward:before,.ion-funnel:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy:before,.ion-happy-outline:before,.ion-headphone:before,.ion-heart:before,.ion-heart-broken:before,.ion-help:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-home:before,.ion-icecream:before,.ion-image:before,.ion-images:before,.ion-information:before,.ion-information-circled:before,.ion-ionic:before,.ion-ios-alarm:before,.ion-ios-alarm-outline:before,.ion-ios-albums:before,.ion-ios-albums-outline:before,.ion-ios-americanfootball:before,.ion-ios-americanfootball-outline:before,.ion-ios-analytics:before,.ion-ios-analytics-outline:before,.ion-ios-arrow-back:before,.ion-ios-arrow-down:before,.ion-ios-arrow-forward:before,.ion-ios-arrow-left:before,.ion-ios-arrow-right:before,.ion-ios-arrow-thin-down:before,.ion-ios-arrow-thin-left:before,.ion-ios-arrow-thin-right:before,.ion-ios-arrow-thin-up:before,.ion-ios-arrow-up:before,.ion-ios-at:before,.ion-ios-at-outline:before,.ion-ios-barcode:before,.ion-ios-barcode-outline:before,.ion-ios-baseball:before,.ion-ios-baseball-outline:before,.ion-ios-basketball:before,.ion-ios-basketball-outline:before,.ion-ios-bell:before,.ion-ios-bell-outline:before,.ion-ios-body:before,.ion-ios-body-outline:before,.ion-ios-bolt:before,.ion-ios-bolt-outline:before,.ion-ios-book:before,.ion-ios-book-outline:before,.ion-ios-bookmarks:before,.ion-ios-bookmarks-outline:before,.ion-ios-box:before,.ion-ios-box-outline:before,.ion-ios-briefcase:before,.ion-ios-briefcase-outline:before,.ion-ios-browsers:before,.ion-ios-browsers-outline:before,.ion-ios-calculator:before,.ion-ios-calculator-outline:before,.ion-ios-calendar:before,.ion-ios-calendar-outline:before,.ion-ios-camera:before,.ion-ios-camera-outline:before,.ion-ios-cart:before,.ion-ios-cart-outline:before,.ion-ios-chatboxes:before,.ion-ios-chatboxes-outline:before,.ion-ios-chatbubble:before,.ion-ios-chatbubble-outline:before,.ion-ios-checkmark:before,.ion-ios-checkmark-empty:before,.ion-ios-checkmark-outline:before,.ion-ios-circle-filled:before,.ion-ios-circle-outline:before,.ion-ios-clock:before,.ion-ios-clock-outline:before,.ion-ios-close:before,.ion-ios-close-empty:before,.ion-ios-close-outline:before,.ion-ios-cloud:before,.ion-ios-cloud-download:before,.ion-ios-cloud-download-outline:before,.ion-ios-cloud-outline:before,.ion-ios-cloud-upload:before,.ion-ios-cloud-upload-outline:before,.ion-ios-cloudy:before,.ion-ios-cloudy-night:before,.ion-ios-cloudy-night-outline:before,.ion-ios-cloudy-outline:before,.ion-ios-cog:before,.ion-ios-cog-outline:before,.ion-ios-color-filter:before,.ion-ios-color-filter-outline:before,.ion-ios-color-wand:before,.ion-ios-color-wand-outline:before,.ion-ios-compose:before,.ion-ios-compose-outline:before,.ion-ios-contact:before,.ion-ios-contact-outline:before,.ion-ios-copy:before,.ion-ios-copy-outline:before,.ion-ios-crop:before,.ion-ios-crop-strong:before,.ion-ios-download:before,.ion-ios-download-outline:before,.ion-ios-drag:before,.ion-ios-email:before,.ion-ios-email-outline:before,.ion-ios-eye:before,.ion-ios-eye-outline:before,.ion-ios-fastforward:before,.ion-ios-fastforward-outline:before,.ion-ios-filing:before,.ion-ios-filing-outline:before,.ion-ios-film:before,.ion-ios-film-outline:before,.ion-ios-flag:before,.ion-ios-flag-outline:before,.ion-ios-flame:before,.ion-ios-flame-outline:before,.ion-ios-flask:before,.ion-ios-flask-outline:before,.ion-ios-flower:before,.ion-ios-flower-outline:before,.ion-ios-folder:before,.ion-ios-folder-outline:before,.ion-ios-football:before,.ion-ios-football-outline:before,.ion-ios-game-controller-a:before,.ion-ios-game-controller-a-outline:before,.ion-ios-game-controller-b:before,.ion-ios-game-controller-b-outline:before,.ion-ios-gear:before,.ion-ios-gear-outline:before,.ion-ios-glasses:before,.ion-ios-glasses-outline:before,.ion-ios-grid-view:before,.ion-ios-grid-view-outline:before,.ion-ios-heart:before,.ion-ios-heart-outline:before,.ion-ios-help:before,.ion-ios-help-empty:before,.ion-ios-help-outline:before,.ion-ios-home:before,.ion-ios-home-outline:before,.ion-ios-infinite:before,.ion-ios-infinite-outline:before,.ion-ios-information:before,.ion-ios-information-empty:before,.ion-ios-information-outline:before,.ion-ios-ionic-outline:before,.ion-ios-keypad:before,.ion-ios-keypad-outline:before,.ion-ios-lightbulb:before,.ion-ios-lightbulb-outline:before,.ion-ios-list:before,.ion-ios-list-outline:before,.ion-ios-location:before,.ion-ios-location-outline:before,.ion-ios-locked:before,.ion-ios-locked-outline:before,.ion-ios-loop:before,.ion-ios-loop-strong:before,.ion-ios-medical:before,.ion-ios-medical-outline:before,.ion-ios-medkit:before,.ion-ios-medkit-outline:before,.ion-ios-mic:before,.ion-ios-mic-off:before,.ion-ios-mic-outline:before,.ion-ios-minus:before,.ion-ios-minus-empty:before,.ion-ios-minus-outline:before,.ion-ios-monitor:before,.ion-ios-monitor-outline:before,.ion-ios-moon:before,.ion-ios-moon-outline:before,.ion-ios-more:before,.ion-ios-more-outline:before,.ion-ios-musical-note:before,.ion-ios-musical-notes:before,.ion-ios-navigate:before,.ion-ios-navigate-outline:before,.ion-ios-nutrition:before,.ion-ios-nutrition-outline:before,.ion-ios-paper:before,.ion-ios-paper-outline:before,.ion-ios-paperplane:before,.ion-ios-paperplane-outline:before,.ion-ios-partlysunny:before,.ion-ios-partlysunny-outline:before,.ion-ios-pause:before,.ion-ios-pause-outline:before,.ion-ios-paw:before,.ion-ios-paw-outline:before,.ion-ios-people:before,.ion-ios-people-outline:before,.ion-ios-person:before,.ion-ios-person-outline:before,.ion-ios-personadd:before,.ion-ios-personadd-outline:before,.ion-ios-photos:before,.ion-ios-photos-outline:before,.ion-ios-pie:before,.ion-ios-pie-outline:before,.ion-ios-pint:before,.ion-ios-pint-outline:before,.ion-ios-play:before,.ion-ios-play-outline:before,.ion-ios-plus:before,.ion-ios-plus-empty:before,.ion-ios-plus-outline:before,.ion-ios-pricetag:before,.ion-ios-pricetag-outline:before,.ion-ios-pricetags:before,.ion-ios-pricetags-outline:before,.ion-ios-printer:before,.ion-ios-printer-outline:before,.ion-ios-pulse:before,.ion-ios-pulse-strong:before,.ion-ios-rainy:before,.ion-ios-rainy-outline:before,.ion-ios-recording:before,.ion-ios-recording-outline:before,.ion-ios-redo:before,.ion-ios-redo-outline:before,.ion-ios-refresh:before,.ion-ios-refresh-empty:before,.ion-ios-refresh-outline:before,.ion-ios-reload:before,.ion-ios-reverse-camera:before,.ion-ios-reverse-camera-outline:before,.ion-ios-rewind:before,.ion-ios-rewind-outline:before,.ion-ios-rose:before,.ion-ios-rose-outline:before,.ion-ios-search:before,.ion-ios-search-strong:before,.ion-ios-settings:before,.ion-ios-settings-strong:before,.ion-ios-shuffle:before,.ion-ios-shuffle-strong:before,.ion-ios-skipbackward:before,.ion-ios-skipbackward-outline:before,.ion-ios-skipforward:before,.ion-ios-skipforward-outline:before,.ion-ios-snowy:before,.ion-ios-speedometer:before,.ion-ios-speedometer-outline:before,.ion-ios-star:before,.ion-ios-star-half:before,.ion-ios-star-outline:before,.ion-ios-stopwatch:before,.ion-ios-stopwatch-outline:before,.ion-ios-sunny:before,.ion-ios-sunny-outline:before,.ion-ios-telephone:before,.ion-ios-telephone-outline:before,.ion-ios-tennisball:before,.ion-ios-tennisball-outline:before,.ion-ios-thunderstorm:before,.ion-ios-thunderstorm-outline:before,.ion-ios-time:before,.ion-ios-time-outline:before,.ion-ios-timer:before,.ion-ios-timer-outline:before,.ion-ios-toggle:before,.ion-ios-toggle-outline:before,.ion-ios-trash:before,.ion-ios-trash-outline:before,.ion-ios-undo:before,.ion-ios-undo-outline:before,.ion-ios-unlocked:before,.ion-ios-unlocked-outline:before,.ion-ios-upload:before,.ion-ios-upload-outline:before,.ion-ios-videocam:before,.ion-ios-videocam-outline:before,.ion-ios-volume-high:before,.ion-ios-volume-low:before,.ion-ios-wineglass:before,.ion-ios-wineglass-outline:before,.ion-ios-world:before,.ion-ios-world-outline:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-lock-combination:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon:before,.ion-navicon-round:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paintbrush:before,.ion-paintbucket:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person:before,.ion-person-add:before,.ion-person-stalker:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply:before,.ion-reply-all:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad:before,.ion-sad-outline:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android:before,.ion-social-android-outline:before,.ion-social-angular:before,.ion-social-angular-outline:before,.ion-social-apple:before,.ion-social-apple-outline:before,.ion-social-bitcoin:before,.ion-social-bitcoin-outline:before,.ion-social-buffer:before,.ion-social-buffer-outline:before,.ion-social-chrome:before,.ion-social-chrome-outline:before,.ion-social-codepen:before,.ion-social-codepen-outline:before,.ion-social-css3:before,.ion-social-css3-outline:before,.ion-social-designernews:before,.ion-social-designernews-outline:before,.ion-social-dribbble:before,.ion-social-dribbble-outline:before,.ion-social-dropbox:before,.ion-social-dropbox-outline:before,.ion-social-euro:before,.ion-social-euro-outline:before,.ion-social-facebook:before,.ion-social-facebook-outline:before,.ion-social-foursquare:before,.ion-social-foursquare-outline:before,.ion-social-freebsd-devil:before,.ion-social-github:before,.ion-social-github-outline:before,.ion-social-google:before,.ion-social-google-outline:before,.ion-social-googleplus:before,.ion-social-googleplus-outline:before,.ion-social-hackernews:before,.ion-social-hackernews-outline:before,.ion-social-html5:before,.ion-social-html5-outline:before,.ion-social-instagram:before,.ion-social-instagram-outline:before,.ion-social-javascript:before,.ion-social-javascript-outline:before,.ion-social-linkedin:before,.ion-social-linkedin-outline:before,.ion-social-markdown:before,.ion-social-nodejs:before,.ion-social-octocat:before,.ion-social-pinterest:before,.ion-social-pinterest-outline:before,.ion-social-python:before,.ion-social-reddit:before,.ion-social-reddit-outline:before,.ion-social-rss:before,.ion-social-rss-outline:before,.ion-social-sass:before,.ion-social-skype:before,.ion-social-skype-outline:before,.ion-social-snapchat:before,.ion-social-snapchat-outline:before,.ion-social-tumblr:before,.ion-social-tumblr-outline:before,.ion-social-tux:before,.ion-social-twitch:before,.ion-social-twitch-outline:before,.ion-social-twitter:before,.ion-social-twitter-outline:before,.ion-social-usd:before,.ion-social-usd-outline:before,.ion-social-vimeo:before,.ion-social-vimeo-outline:before,.ion-social-whatsapp:before,.ion-social-whatsapp-outline:before,.ion-social-windows:before,.ion-social-windows-outline:before,.ion-social-wordpress:before,.ion-social-wordpress-outline:before,.ion-social-yahoo:before,.ion-social-yahoo-outline:before,.ion-social-yen:before,.ion-social-yen-outline:before,.ion-social-youtube:before,.ion-social-youtube-outline:before,.ion-soup-can:before,.ion-soup-can-outline:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle:before,.ion-toggle-filled:before,.ion-transgender:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-tshirt:before,.ion-tshirt-outline:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before{display:inline-block;font-family:"Ionicons";speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-alert:before{content:"\f101"}.ion-alert-circled:before{content:"\f100"}.ion-android-add:before{content:"\f2c7"}.ion-android-add-circle:before{content:"\f359"}.ion-android-alarm-clock:before{content:"\f35a"}.ion-android-alert:before{content:"\f35b"}.ion-android-apps:before{content:"\f35c"}.ion-android-archive:before{content:"\f2c9"}.ion-android-arrow-back:before{content:"\f2ca"}.ion-android-arrow-down:before{content:"\f35d"}.ion-android-arrow-dropdown:before{content:"\f35f"}.ion-android-arrow-dropdown-circle:before{content:"\f35e"}.ion-android-arrow-dropleft:before{content:"\f361"}.ion-android-arrow-dropleft-circle:before{content:"\f360"}.ion-android-arrow-dropright:before{content:"\f363"}.ion-android-arrow-dropright-circle:before{content:"\f362"}.ion-android-arrow-dropup:before{content:"\f365"}.ion-android-arrow-dropup-circle:before{content:"\f364"}.ion-android-arrow-forward:before{content:"\f30f"}.ion-android-arrow-up:before{content:"\f366"}.ion-android-attach:before{content:"\f367"}.ion-android-bar:before{content:"\f368"}.ion-android-bicycle:before{content:"\f369"}.ion-android-boat:before{content:"\f36a"}.ion-android-bookmark:before{content:"\f36b"}.ion-android-bulb:before{content:"\f36c"}.ion-android-bus:before{content:"\f36d"}.ion-android-calendar:before{content:"\f2d1"}.ion-android-call:before{content:"\f2d2"}.ion-android-camera:before{content:"\f2d3"}.ion-android-cancel:before{content:"\f36e"}.ion-android-car:before{content:"\f36f"}.ion-android-cart:before{content:"\f370"}.ion-android-chat:before{content:"\f2d4"}.ion-android-checkbox:before{content:"\f374"}.ion-android-checkbox-blank:before{content:"\f371"}.ion-android-checkbox-outline:before{content:"\f373"}.ion-android-checkbox-outline-blank:before{content:"\f372"}.ion-android-checkmark-circle:before{content:"\f375"}.ion-android-clipboard:before{content:"\f376"}.ion-android-close:before{content:"\f2d7"}.ion-android-cloud:before{content:"\f37a"}.ion-android-cloud-circle:before{content:"\f377"}.ion-android-cloud-done:before{content:"\f378"}.ion-android-cloud-outline:before{content:"\f379"}.ion-android-color-palette:before{content:"\f37b"}.ion-android-compass:before{content:"\f37c"}.ion-android-contact:before{content:"\f2d8"}.ion-android-contacts:before{content:"\f2d9"}.ion-android-contract:before{content:"\f37d"}.ion-android-create:before{content:"\f37e"}.ion-android-delete:before{content:"\f37f"}.ion-android-desktop:before{content:"\f380"}.ion-android-document:before{content:"\f381"}.ion-android-done:before{content:"\f383"}.ion-android-done-all:before{content:"\f382"}.ion-android-download:before{content:"\f2dd"}.ion-android-drafts:before{content:"\f384"}.ion-android-exit:before{content:"\f385"}.ion-android-expand:before{content:"\f386"}.ion-android-favorite:before{content:"\f388"}.ion-android-favorite-outline:before{content:"\f387"}.ion-android-film:before{content:"\f389"}.ion-android-folder:before{content:"\f2e0"}.ion-android-folder-open:before{content:"\f38a"}.ion-android-funnel:before{content:"\f38b"}.ion-android-globe:before{content:"\f38c"}.ion-android-hand:before{content:"\f2e3"}.ion-android-hangout:before{content:"\f38d"}.ion-android-happy:before{content:"\f38e"}.ion-android-home:before{content:"\f38f"}.ion-android-image:before{content:"\f2e4"}.ion-android-laptop:before{content:"\f390"}.ion-android-list:before{content:"\f391"}.ion-android-locate:before{content:"\f2e9"}.ion-android-lock:before{content:"\f392"}.ion-android-mail:before{content:"\f2eb"}.ion-android-map:before{content:"\f393"}.ion-android-menu:before{content:"\f394"}.ion-android-microphone:before{content:"\f2ec"}.ion-android-microphone-off:before{content:"\f395"}.ion-android-more-horizontal:before{content:"\f396"}.ion-android-more-vertical:before{content:"\f397"}.ion-android-navigate:before{content:"\f398"}.ion-android-notifications:before{content:"\f39b"}.ion-android-notifications-none:before{content:"\f399"}.ion-android-notifications-off:before{content:"\f39a"}.ion-android-open:before{content:"\f39c"}.ion-android-options:before{content:"\f39d"}.ion-android-people:before{content:"\f39e"}.ion-android-person:before{content:"\f3a0"}.ion-android-person-add:before{content:"\f39f"}.ion-android-phone-landscape:before{content:"\f3a1"}.ion-android-phone-portrait:before{content:"\f3a2"}.ion-android-pin:before{content:"\f3a3"}.ion-android-plane:before{content:"\f3a4"}.ion-android-playstore:before{content:"\f2f0"}.ion-android-print:before{content:"\f3a5"}.ion-android-radio-button-off:before{content:"\f3a6"}.ion-android-radio-button-on:before{content:"\f3a7"}.ion-android-refresh:before{content:"\f3a8"}.ion-android-remove:before{content:"\f2f4"}.ion-android-remove-circle:before{content:"\f3a9"}.ion-android-restaurant:before{content:"\f3aa"}.ion-android-sad:before{content:"\f3ab"}.ion-android-search:before{content:"\f2f5"}.ion-android-send:before{content:"\f2f6"}.ion-android-settings:before{content:"\f2f7"}.ion-android-share:before{content:"\f2f8"}.ion-android-share-alt:before{content:"\f3ac"}.ion-android-star:before{content:"\f2fc"}.ion-android-star-half:before{content:"\f3ad"}.ion-android-star-outline:before{content:"\f3ae"}.ion-android-stopwatch:before{content:"\f2fd"}.ion-android-subway:before{content:"\f3af"}.ion-android-sunny:before{content:"\f3b0"}.ion-android-sync:before{content:"\f3b1"}.ion-android-textsms:before{content:"\f3b2"}.ion-android-time:before{content:"\f3b3"}.ion-android-train:before{content:"\f3b4"}.ion-android-unlock:before{content:"\f3b5"}.ion-android-upload:before{content:"\f3b6"}.ion-android-volume-down:before{content:"\f3b7"}.ion-android-volume-mute:before{content:"\f3b8"}.ion-android-volume-off:before{content:"\f3b9"}.ion-android-volume-up:before{content:"\f3ba"}.ion-android-walk:before{content:"\f3bb"}.ion-android-warning:before{content:"\f3bc"}.ion-android-watch:before{content:"\f3bd"}.ion-android-wifi:before{content:"\f305"}.ion-aperture:before{content:"\f313"}.ion-archive:before{content:"\f102"}.ion-arrow-down-a:before{content:"\f103"}.ion-arrow-down-b:before{content:"\f104"}.ion-arrow-down-c:before{content:"\f105"}.ion-arrow-expand:before{content:"\f25e"}.ion-arrow-graph-down-left:before{content:"\f25f"}.ion-arrow-graph-down-right:before{content:"\f260"}.ion-arrow-graph-up-left:before{content:"\f261"}.ion-arrow-graph-up-right:before{content:"\f262"}.ion-arrow-left-a:before{content:"\f106"}.ion-arrow-left-b:before{content:"\f107"}.ion-arrow-left-c:before{content:"\f108"}.ion-arrow-move:before{content:"\f263"}.ion-arrow-resize:before{content:"\f264"}.ion-arrow-return-left:before{content:"\f265"}.ion-arrow-return-right:before{content:"\f266"}.ion-arrow-right-a:before{content:"\f109"}.ion-arrow-right-b:before{content:"\f10a"}.ion-arrow-right-c:before{content:"\f10b"}.ion-arrow-shrink:before{content:"\f267"}.ion-arrow-swap:before{content:"\f268"}.ion-arrow-up-a:before{content:"\f10c"}.ion-arrow-up-b:before{content:"\f10d"}.ion-arrow-up-c:before{content:"\f10e"}.ion-asterisk:before{content:"\f314"}.ion-at:before{content:"\f10f"}.ion-backspace:before{content:"\f3bf"}.ion-backspace-outline:before{content:"\f3be"}.ion-bag:before{content:"\f110"}.ion-battery-charging:before{content:"\f111"}.ion-battery-empty:before{content:"\f112"}.ion-battery-full:before{content:"\f113"}.ion-battery-half:before{content:"\f114"}.ion-battery-low:before{content:"\f115"}.ion-beaker:before{content:"\f269"}.ion-beer:before{content:"\f26a"}.ion-bluetooth:before{content:"\f116"}.ion-bonfire:before{content:"\f315"}.ion-bookmark:before{content:"\f26b"}.ion-bowtie:before{content:"\f3c0"}.ion-briefcase:before{content:"\f26c"}.ion-bug:before{content:"\f2be"}.ion-calculator:before{content:"\f26d"}.ion-calendar:before{content:"\f117"}.ion-camera:before{content:"\f118"}.ion-card:before{content:"\f119"}.ion-cash:before{content:"\f316"}.ion-chatbox:before{content:"\f11b"}.ion-chatbox-working:before{content:"\f11a"}.ion-chatboxes:before{content:"\f11c"}.ion-chatbubble:before{content:"\f11e"}.ion-chatbubble-working:before{content:"\f11d"}.ion-chatbubbles:before{content:"\f11f"}.ion-checkmark:before{content:"\f122"}.ion-checkmark-circled:before{content:"\f120"}.ion-checkmark-round:before{content:"\f121"}.ion-chevron-down:before{content:"\f123"}.ion-chevron-left:before{content:"\f124"}.ion-chevron-right:before{content:"\f125"}.ion-chevron-up:before{content:"\f126"}.ion-clipboard:before{content:"\f127"}.ion-clock:before{content:"\f26e"}.ion-close:before{content:"\f12a"}.ion-close-circled:before{content:"\f128"}.ion-close-round:before{content:"\f129"}.ion-closed-captioning:before{content:"\f317"}.ion-cloud:before{content:"\f12b"}.ion-code:before{content:"\f271"}.ion-code-download:before{content:"\f26f"}.ion-code-working:before{content:"\f270"}.ion-coffee:before{content:"\f272"}.ion-compass:before{content:"\f273"}.ion-compose:before{content:"\f12c"}.ion-connection-bars:before{content:"\f274"}.ion-contrast:before{content:"\f275"}.ion-crop:before{content:"\f3c1"}.ion-cube:before{content:"\f318"}.ion-disc:before{content:"\f12d"}.ion-document:before{content:"\f12f"}.ion-document-text:before{content:"\f12e"}.ion-drag:before{content:"\f130"}.ion-earth:before{content:"\f276"}.ion-easel:before{content:"\f3c2"}.ion-edit:before{content:"\f2bf"}.ion-egg:before{content:"\f277"}.ion-eject:before{content:"\f131"}.ion-email:before{content:"\f132"}.ion-email-unread:before{content:"\f3c3"}.ion-erlenmeyer-flask:before{content:"\f3c5"}.ion-erlenmeyer-flask-bubbles:before{content:"\f3c4"}.ion-eye:before{content:"\f133"}.ion-eye-disabled:before{content:"\f306"}.ion-female:before{content:"\f278"}.ion-filing:before{content:"\f134"}.ion-film-marker:before{content:"\f135"}.ion-fireball:before{content:"\f319"}.ion-flag:before{content:"\f279"}.ion-flame:before{content:"\f31a"}.ion-flash:before{content:"\f137"}.ion-flash-off:before{content:"\f136"}.ion-folder:before{content:"\f139"}.ion-fork:before{content:"\f27a"}.ion-fork-repo:before{content:"\f2c0"}.ion-forward:before{content:"\f13a"}.ion-funnel:before{content:"\f31b"}.ion-gear-a:before{content:"\f13d"}.ion-gear-b:before{content:"\f13e"}.ion-grid:before{content:"\f13f"}.ion-hammer:before{content:"\f27b"}.ion-happy:before{content:"\f31c"}.ion-happy-outline:before{content:"\f3c6"}.ion-headphone:before{content:"\f140"}.ion-heart:before{content:"\f141"}.ion-heart-broken:before{content:"\f31d"}.ion-help:before{content:"\f143"}.ion-help-buoy:before{content:"\f27c"}.ion-help-circled:before{content:"\f142"}.ion-home:before{content:"\f144"}.ion-icecream:before{content:"\f27d"}.ion-image:before{content:"\f147"}.ion-images:before{content:"\f148"}.ion-information:before{content:"\f14a"}.ion-information-circled:before{content:"\f149"}.ion-ionic:before{content:"\f14b"}.ion-ios-alarm:before{content:"\f3c8"}.ion-ios-alarm-outline:before{content:"\f3c7"}.ion-ios-albums:before{content:"\f3ca"}.ion-ios-albums-outline:before{content:"\f3c9"}.ion-ios-americanfootball:before{content:"\f3cc"}.ion-ios-americanfootball-outline:before{content:"\f3cb"}.ion-ios-analytics:before{content:"\f3ce"}.ion-ios-analytics-outline:before{content:"\f3cd"}.ion-ios-arrow-back:before{content:"\f3cf"}.ion-ios-arrow-down:before{content:"\f3d0"}.ion-ios-arrow-forward:before{content:"\f3d1"}.ion-ios-arrow-left:before{content:"\f3d2"}.ion-ios-arrow-right:before{content:"\f3d3"}.ion-ios-arrow-thin-down:before{content:"\f3d4"}.ion-ios-arrow-thin-left:before{content:"\f3d5"}.ion-ios-arrow-thin-right:before{content:"\f3d6"}.ion-ios-arrow-thin-up:before{content:"\f3d7"}.ion-ios-arrow-up:before{content:"\f3d8"}.ion-ios-at:before{content:"\f3da"}.ion-ios-at-outline:before{content:"\f3d9"}.ion-ios-barcode:before{content:"\f3dc"}.ion-ios-barcode-outline:before{content:"\f3db"}.ion-ios-baseball:before{content:"\f3de"}.ion-ios-baseball-outline:before{content:"\f3dd"}.ion-ios-basketball:before{content:"\f3e0"}.ion-ios-basketball-outline:before{content:"\f3df"}.ion-ios-bell:before{content:"\f3e2"}.ion-ios-bell-outline:before{content:"\f3e1"}.ion-ios-body:before{content:"\f3e4"}.ion-ios-body-outline:before{content:"\f3e3"}.ion-ios-bolt:before{content:"\f3e6"}.ion-ios-bolt-outline:before{content:"\f3e5"}.ion-ios-book:before{content:"\f3e8"}.ion-ios-book-outline:before{content:"\f3e7"}.ion-ios-bookmarks:before{content:"\f3ea"}.ion-ios-bookmarks-outline:before{content:"\f3e9"}.ion-ios-box:before{content:"\f3ec"}.ion-ios-box-outline:before{content:"\f3eb"}.ion-ios-briefcase:before{content:"\f3ee"}.ion-ios-briefcase-outline:before{content:"\f3ed"}.ion-ios-browsers:before{content:"\f3f0"}.ion-ios-browsers-outline:before{content:"\f3ef"}.ion-ios-calculator:before{content:"\f3f2"}.ion-ios-calculator-outline:before{content:"\f3f1"}.ion-ios-calendar:before{content:"\f3f4"}.ion-ios-calendar-outline:before{content:"\f3f3"}.ion-ios-camera:before{content:"\f3f6"}.ion-ios-camera-outline:before{content:"\f3f5"}.ion-ios-cart:before{content:"\f3f8"}.ion-ios-cart-outline:before{content:"\f3f7"}.ion-ios-chatboxes:before{content:"\f3fa"}.ion-ios-chatboxes-outline:before{content:"\f3f9"}.ion-ios-chatbubble:before{content:"\f3fc"}.ion-ios-chatbubble-outline:before{content:"\f3fb"}.ion-ios-checkmark:before{content:"\f3ff"}.ion-ios-checkmark-empty:before{content:"\f3fd"}.ion-ios-checkmark-outline:before{content:"\f3fe"}.ion-ios-circle-filled:before{content:"\f400"}.ion-ios-circle-outline:before{content:"\f401"}.ion-ios-clock:before{content:"\f403"}.ion-ios-clock-outline:before{content:"\f402"}.ion-ios-close:before{content:"\f406"}.ion-ios-close-empty:before{content:"\f404"}.ion-ios-close-outline:before{content:"\f405"}.ion-ios-cloud:before{content:"\f40c"}.ion-ios-cloud-download:before{content:"\f408"}.ion-ios-cloud-download-outline:before{content:"\f407"}.ion-ios-cloud-outline:before{content:"\f409"}.ion-ios-cloud-upload:before{content:"\f40b"}.ion-ios-cloud-upload-outline:before{content:"\f40a"}.ion-ios-cloudy:before{content:"\f410"}.ion-ios-cloudy-night:before{content:"\f40e"}.ion-ios-cloudy-night-outline:before{content:"\f40d"}.ion-ios-cloudy-outline:before{content:"\f40f"}.ion-ios-cog:before{content:"\f412"}.ion-ios-cog-outline:before{content:"\f411"}.ion-ios-color-filter:before{content:"\f414"}.ion-ios-color-filter-outline:before{content:"\f413"}.ion-ios-color-wand:before{content:"\f416"}.ion-ios-color-wand-outline:before{content:"\f415"}.ion-ios-compose:before{content:"\f418"}.ion-ios-compose-outline:before{content:"\f417"}.ion-ios-contact:before{content:"\f41a"}.ion-ios-contact-outline:before{content:"\f419"}.ion-ios-copy:before{content:"\f41c"}.ion-ios-copy-outline:before{content:"\f41b"}.ion-ios-crop:before{content:"\f41e"}.ion-ios-crop-strong:before{content:"\f41d"}.ion-ios-download:before{content:"\f420"}.ion-ios-download-outline:before{content:"\f41f"}.ion-ios-drag:before{content:"\f421"}.ion-ios-email:before{content:"\f423"}.ion-ios-email-outline:before{content:"\f422"}.ion-ios-eye:before{content:"\f425"}.ion-ios-eye-outline:before{content:"\f424"}.ion-ios-fastforward:before{content:"\f427"}.ion-ios-fastforward-outline:before{content:"\f426"}.ion-ios-filing:before{content:"\f429"}.ion-ios-filing-outline:before{content:"\f428"}.ion-ios-film:before{content:"\f42b"}.ion-ios-film-outline:before{content:"\f42a"}.ion-ios-flag:before{content:"\f42d"}.ion-ios-flag-outline:before{content:"\f42c"}.ion-ios-flame:before{content:"\f42f"}.ion-ios-flame-outline:before{content:"\f42e"}.ion-ios-flask:before{content:"\f431"}.ion-ios-flask-outline:before{content:"\f430"}.ion-ios-flower:before{content:"\f433"}.ion-ios-flower-outline:before{content:"\f432"}.ion-ios-folder:before{content:"\f435"}.ion-ios-folder-outline:before{content:"\f434"}.ion-ios-football:before{content:"\f437"}.ion-ios-football-outline:before{content:"\f436"}.ion-ios-game-controller-a:before{content:"\f439"}.ion-ios-game-controller-a-outline:before{content:"\f438"}.ion-ios-game-controller-b:before{content:"\f43b"}.ion-ios-game-controller-b-outline:before{content:"\f43a"}.ion-ios-gear:before{content:"\f43d"}.ion-ios-gear-outline:before{content:"\f43c"}.ion-ios-glasses:before{content:"\f43f"}.ion-ios-glasses-outline:before{content:"\f43e"}.ion-ios-grid-view:before{content:"\f441"}.ion-ios-grid-view-outline:before{content:"\f440"}.ion-ios-heart:before{content:"\f443"}.ion-ios-heart-outline:before{content:"\f442"}.ion-ios-help:before{content:"\f446"}.ion-ios-help-empty:before{content:"\f444"}.ion-ios-help-outline:before{content:"\f445"}.ion-ios-home:before{content:"\f448"}.ion-ios-home-outline:before{content:"\f447"}.ion-ios-infinite:before{content:"\f44a"}.ion-ios-infinite-outline:before{content:"\f449"}.ion-ios-information:before{content:"\f44d"}.ion-ios-information-empty:before{content:"\f44b"}.ion-ios-information-outline:before{content:"\f44c"}.ion-ios-ionic-outline:before{content:"\f44e"}.ion-ios-keypad:before{content:"\f450"}.ion-ios-keypad-outline:before{content:"\f44f"}.ion-ios-lightbulb:before{content:"\f452"}.ion-ios-lightbulb-outline:before{content:"\f451"}.ion-ios-list:before{content:"\f454"}.ion-ios-list-outline:before{content:"\f453"}.ion-ios-location:before{content:"\f456"}.ion-ios-location-outline:before{content:"\f455"}.ion-ios-locked:before{content:"\f458"}.ion-ios-locked-outline:before{content:"\f457"}.ion-ios-loop:before{content:"\f45a"}.ion-ios-loop-strong:before{content:"\f459"}.ion-ios-medical:before{content:"\f45c"}.ion-ios-medical-outline:before{content:"\f45b"}.ion-ios-medkit:before{content:"\f45e"}.ion-ios-medkit-outline:before{content:"\f45d"}.ion-ios-mic:before{content:"\f461"}.ion-ios-mic-off:before{content:"\f45f"}.ion-ios-mic-outline:before{content:"\f460"}.ion-ios-minus:before{content:"\f464"}.ion-ios-minus-empty:before{content:"\f462"}.ion-ios-minus-outline:before{content:"\f463"}.ion-ios-monitor:before{content:"\f466"}.ion-ios-monitor-outline:before{content:"\f465"}.ion-ios-moon:before{content:"\f468"}.ion-ios-moon-outline:before{content:"\f467"}.ion-ios-more:before{content:"\f46a"}.ion-ios-more-outline:before{content:"\f469"}.ion-ios-musical-note:before{content:"\f46b"}.ion-ios-musical-notes:before{content:"\f46c"}.ion-ios-navigate:before{content:"\f46e"}.ion-ios-navigate-outline:before{content:"\f46d"}.ion-ios-nutrition:before{content:"\f470"}.ion-ios-nutrition-outline:before{content:"\f46f"}.ion-ios-paper:before{content:"\f472"}.ion-ios-paper-outline:before{content:"\f471"}.ion-ios-paperplane:before{content:"\f474"}.ion-ios-paperplane-outline:before{content:"\f473"}.ion-ios-partlysunny:before{content:"\f476"}.ion-ios-partlysunny-outline:before{content:"\f475"}.ion-ios-pause:before{content:"\f478"}.ion-ios-pause-outline:before{content:"\f477"}.ion-ios-paw:before{content:"\f47a"}.ion-ios-paw-outline:before{content:"\f479"}.ion-ios-people:before{content:"\f47c"}.ion-ios-people-outline:before{content:"\f47b"}.ion-ios-person:before{content:"\f47e"}.ion-ios-person-outline:before{content:"\f47d"}.ion-ios-personadd:before{content:"\f480"}.ion-ios-personadd-outline:before{content:"\f47f"}.ion-ios-photos:before{content:"\f482"}.ion-ios-photos-outline:before{content:"\f481"}.ion-ios-pie:before{content:"\f484"}.ion-ios-pie-outline:before{content:"\f483"}.ion-ios-pint:before{content:"\f486"}.ion-ios-pint-outline:before{content:"\f485"}.ion-ios-play:before{content:"\f488"}.ion-ios-play-outline:before{content:"\f487"}.ion-ios-plus:before{content:"\f48b"}.ion-ios-plus-empty:before{content:"\f489"}.ion-ios-plus-outline:before{content:"\f48a"}.ion-ios-pricetag:before{content:"\f48d"}.ion-ios-pricetag-outline:before{content:"\f48c"}.ion-ios-pricetags:before{content:"\f48f"}.ion-ios-pricetags-outline:before{content:"\f48e"}.ion-ios-printer:before{content:"\f491"}.ion-ios-printer-outline:before{content:"\f490"}.ion-ios-pulse:before{content:"\f493"}.ion-ios-pulse-strong:before{content:"\f492"}.ion-ios-rainy:before{content:"\f495"}.ion-ios-rainy-outline:before{content:"\f494"}.ion-ios-recording:before{content:"\f497"}.ion-ios-recording-outline:before{content:"\f496"}.ion-ios-redo:before{content:"\f499"}.ion-ios-redo-outline:before{content:"\f498"}.ion-ios-refresh:before{content:"\f49c"}.ion-ios-refresh-empty:before{content:"\f49a"}.ion-ios-refresh-outline:before{content:"\f49b"}.ion-ios-reload:before{content:"\f49d"}.ion-ios-reverse-camera:before{content:"\f49f"}.ion-ios-reverse-camera-outline:before{content:"\f49e"}.ion-ios-rewind:before{content:"\f4a1"}.ion-ios-rewind-outline:before{content:"\f4a0"}.ion-ios-rose:before{content:"\f4a3"}.ion-ios-rose-outline:before{content:"\f4a2"}.ion-ios-search:before{content:"\f4a5"}.ion-ios-search-strong:before{content:"\f4a4"}.ion-ios-settings:before{content:"\f4a7"}.ion-ios-settings-strong:before{content:"\f4a6"}.ion-ios-shuffle:before{content:"\f4a9"}.ion-ios-shuffle-strong:before{content:"\f4a8"}.ion-ios-skipbackward:before{content:"\f4ab"}.ion-ios-skipbackward-outline:before{content:"\f4aa"}.ion-ios-skipforward:before{content:"\f4ad"}.ion-ios-skipforward-outline:before{content:"\f4ac"}.ion-ios-snowy:before{content:"\f4ae"}.ion-ios-speedometer:before{content:"\f4b0"}.ion-ios-speedometer-outline:before{content:"\f4af"}.ion-ios-star:before{content:"\f4b3"}.ion-ios-star-half:before{content:"\f4b1"}.ion-ios-star-outline:before{content:"\f4b2"}.ion-ios-stopwatch:before{content:"\f4b5"}.ion-ios-stopwatch-outline:before{content:"\f4b4"}.ion-ios-sunny:before{content:"\f4b7"}.ion-ios-sunny-outline:before{content:"\f4b6"}.ion-ios-telephone:before{content:"\f4b9"}.ion-ios-telephone-outline:before{content:"\f4b8"}.ion-ios-tennisball:before{content:"\f4bb"}.ion-ios-tennisball-outline:before{content:"\f4ba"}.ion-ios-thunderstorm:before{content:"\f4bd"}.ion-ios-thunderstorm-outline:before{content:"\f4bc"}.ion-ios-time:before{content:"\f4bf"}.ion-ios-time-outline:before{content:"\f4be"}.ion-ios-timer:before{content:"\f4c1"}.ion-ios-timer-outline:before{content:"\f4c0"}.ion-ios-toggle:before{content:"\f4c3"}.ion-ios-toggle-outline:before{content:"\f4c2"}.ion-ios-trash:before{content:"\f4c5"}.ion-ios-trash-outline:before{content:"\f4c4"}.ion-ios-undo:before{content:"\f4c7"}.ion-ios-undo-outline:before{content:"\f4c6"}.ion-ios-unlocked:before{content:"\f4c9"}.ion-ios-unlocked-outline:before{content:"\f4c8"}.ion-ios-upload:before{content:"\f4cb"}.ion-ios-upload-outline:before{content:"\f4ca"}.ion-ios-videocam:before{content:"\f4cd"}.ion-ios-videocam-outline:before{content:"\f4cc"}.ion-ios-volume-high:before{content:"\f4ce"}.ion-ios-volume-low:before{content:"\f4cf"}.ion-ios-wineglass:before{content:"\f4d1"}.ion-ios-wineglass-outline:before{content:"\f4d0"}.ion-ios-world:before{content:"\f4d3"}.ion-ios-world-outline:before{content:"\f4d2"}.ion-ipad:before{content:"\f1f9"}.ion-iphone:before{content:"\f1fa"}.ion-ipod:before{content:"\f1fb"}.ion-jet:before{content:"\f295"}.ion-key:before{content:"\f296"}.ion-knife:before{content:"\f297"}.ion-laptop:before{content:"\f1fc"}.ion-leaf:before{content:"\f1fd"}.ion-levels:before{content:"\f298"}.ion-lightbulb:before{content:"\f299"}.ion-link:before{content:"\f1fe"}.ion-load-a:before{content:"\f29a"}.ion-load-b:before{content:"\f29b"}.ion-load-c:before{content:"\f29c"}.ion-load-d:before{content:"\f29d"}.ion-location:before{content:"\f1ff"}.ion-lock-combination:before{content:"\f4d4"}.ion-locked:before{content:"\f200"}.ion-log-in:before{content:"\f29e"}.ion-log-out:before{content:"\f29f"}.ion-loop:before{content:"\f201"}.ion-magnet:before{content:"\f2a0"}.ion-male:before{content:"\f2a1"}.ion-man:before{content:"\f202"}.ion-map:before{content:"\f203"}.ion-medkit:before{content:"\f2a2"}.ion-merge:before{content:"\f33f"}.ion-mic-a:before{content:"\f204"}.ion-mic-b:before{content:"\f205"}.ion-mic-c:before{content:"\f206"}.ion-minus:before{content:"\f209"}.ion-minus-circled:before{content:"\f207"}.ion-minus-round:before{content:"\f208"}.ion-model-s:before{content:"\f2c1"}.ion-monitor:before{content:"\f20a"}.ion-more:before{content:"\f20b"}.ion-mouse:before{content:"\f340"}.ion-music-note:before{content:"\f20c"}.ion-navicon:before{content:"\f20e"}.ion-navicon-round:before{content:"\f20d"}.ion-navigate:before{content:"\f2a3"}.ion-network:before{content:"\f341"}.ion-no-smoking:before{content:"\f2c2"}.ion-nuclear:before{content:"\f2a4"}.ion-outlet:before{content:"\f342"}.ion-paintbrush:before{content:"\f4d5"}.ion-paintbucket:before{content:"\f4d6"}.ion-paper-airplane:before{content:"\f2c3"}.ion-paperclip:before{content:"\f20f"}.ion-pause:before{content:"\f210"}.ion-person:before{content:"\f213"}.ion-person-add:before{content:"\f211"}.ion-person-stalker:before{content:"\f212"}.ion-pie-graph:before{content:"\f2a5"}.ion-pin:before{content:"\f2a6"}.ion-pinpoint:before{content:"\f2a7"}.ion-pizza:before{content:"\f2a8"}.ion-plane:before{content:"\f214"}.ion-planet:before{content:"\f343"}.ion-play:before{content:"\f215"}.ion-playstation:before{content:"\f30a"}.ion-plus:before{content:"\f218"}.ion-plus-circled:before{content:"\f216"}.ion-plus-round:before{content:"\f217"}.ion-podium:before{content:"\f344"}.ion-pound:before{content:"\f219"}.ion-power:before{content:"\f2a9"}.ion-pricetag:before{content:"\f2aa"}.ion-pricetags:before{content:"\f2ab"}.ion-printer:before{content:"\f21a"}.ion-pull-request:before{content:"\f345"}.ion-qr-scanner:before{content:"\f346"}.ion-quote:before{content:"\f347"}.ion-radio-waves:before{content:"\f2ac"}.ion-record:before{content:"\f21b"}.ion-refresh:before{content:"\f21c"}.ion-reply:before{content:"\f21e"}.ion-reply-all:before{content:"\f21d"}.ion-ribbon-a:before{content:"\f348"}.ion-ribbon-b:before{content:"\f349"}.ion-sad:before{content:"\f34a"}.ion-sad-outline:before{content:"\f4d7"}.ion-scissors:before{content:"\f34b"}.ion-search:before{content:"\f21f"}.ion-settings:before{content:"\f2ad"}.ion-share:before{content:"\f220"}.ion-shuffle:before{content:"\f221"}.ion-skip-backward:before{content:"\f222"}.ion-skip-forward:before{content:"\f223"}.ion-social-android:before{content:"\f225"}.ion-social-android-outline:before{content:"\f224"}.ion-social-angular:before{content:"\f4d9"}.ion-social-angular-outline:before{content:"\f4d8"}.ion-social-apple:before{content:"\f227"}.ion-social-apple-outline:before{content:"\f226"}.ion-social-bitcoin:before{content:"\f2af"}.ion-social-bitcoin-outline:before{content:"\f2ae"}.ion-social-buffer:before{content:"\f229"}.ion-social-buffer-outline:before{content:"\f228"}.ion-social-chrome:before{content:"\f4db"}.ion-social-chrome-outline:before{content:"\f4da"}.ion-social-codepen:before{content:"\f4dd"}.ion-social-codepen-outline:before{content:"\f4dc"}.ion-social-css3:before{content:"\f4df"}.ion-social-css3-outline:before{content:"\f4de"}.ion-social-designernews:before{content:"\f22b"}.ion-social-designernews-outline:before{content:"\f22a"}.ion-social-dribbble:before{content:"\f22d"}.ion-social-dribbble-outline:before{content:"\f22c"}.ion-social-dropbox:before{content:"\f22f"}.ion-social-dropbox-outline:before{content:"\f22e"}.ion-social-euro:before{content:"\f4e1"}.ion-social-euro-outline:before{content:"\f4e0"}.ion-social-facebook:before{content:"\f231"}.ion-social-facebook-outline:before{content:"\f230"}.ion-social-foursquare:before{content:"\f34d"}.ion-social-foursquare-outline:before{content:"\f34c"}.ion-social-freebsd-devil:before{content:"\f2c4"}.ion-social-github:before{content:"\f233"}.ion-social-github-outline:before{content:"\f232"}.ion-social-google:before{content:"\f34f"}.ion-social-google-outline:before{content:"\f34e"}.ion-social-googleplus:before{content:"\f235"}.ion-social-googleplus-outline:before{content:"\f234"}.ion-social-hackernews:before{content:"\f237"}.ion-social-hackernews-outline:before{content:"\f236"}.ion-social-html5:before{content:"\f4e3"}.ion-social-html5-outline:before{content:"\f4e2"}.ion-social-instagram:before{content:"\f351"}.ion-social-instagram-outline:before{content:"\f350"}.ion-social-javascript:before{content:"\f4e5"}.ion-social-javascript-outline:before{content:"\f4e4"}.ion-social-linkedin:before{content:"\f239"}.ion-social-linkedin-outline:before{content:"\f238"}.ion-social-markdown:before{content:"\f4e6"}.ion-social-nodejs:before{content:"\f4e7"}.ion-social-octocat:before{content:"\f4e8"}.ion-social-pinterest:before{content:"\f2b1"}.ion-social-pinterest-outline:before{content:"\f2b0"}.ion-social-python:before{content:"\f4e9"}.ion-social-reddit:before{content:"\f23b"}.ion-social-reddit-outline:before{content:"\f23a"}.ion-social-rss:before{content:"\f23d"}.ion-social-rss-outline:before{content:"\f23c"}.ion-social-sass:before{content:"\f4ea"}.ion-social-skype:before{content:"\f23f"}.ion-social-skype-outline:before{content:"\f23e"}.ion-social-snapchat:before{content:"\f4ec"}.ion-social-snapchat-outline:before{content:"\f4eb"}.ion-social-tumblr:before{content:"\f241"}.ion-social-tumblr-outline:before{content:"\f240"}.ion-social-tux:before{content:"\f2c5"}.ion-social-twitch:before{content:"\f4ee"}.ion-social-twitch-outline:before{content:"\f4ed"}.ion-social-twitter:before{content:"\f243"}.ion-social-twitter-outline:before{content:"\f242"}.ion-social-usd:before{content:"\f353"}.ion-social-usd-outline:before{content:"\f352"}.ion-social-vimeo:before{content:"\f245"}.ion-social-vimeo-outline:before{content:"\f244"}.ion-social-whatsapp:before{content:"\f4f0"}.ion-social-whatsapp-outline:before{content:"\f4ef"}.ion-social-windows:before{content:"\f247"}.ion-social-windows-outline:before{content:"\f246"}.ion-social-wordpress:before{content:"\f249"}.ion-social-wordpress-outline:before{content:"\f248"}.ion-social-yahoo:before{content:"\f24b"}.ion-social-yahoo-outline:before{content:"\f24a"}.ion-social-yen:before{content:"\f4f2"}.ion-social-yen-outline:before{content:"\f4f1"}.ion-social-youtube:before{content:"\f24d"}.ion-social-youtube-outline:before{content:"\f24c"}.ion-soup-can:before{content:"\f4f4"}.ion-soup-can-outline:before{content:"\f4f3"}.ion-speakerphone:before{content:"\f2b2"}.ion-speedometer:before{content:"\f2b3"}.ion-spoon:before{content:"\f2b4"}.ion-star:before{content:"\f24e"}.ion-stats-bars:before{content:"\f2b5"}.ion-steam:before{content:"\f30b"}.ion-stop:before{content:"\f24f"}.ion-thermometer:before{content:"\f2b6"}.ion-thumbsdown:before{content:"\f250"}.ion-thumbsup:before{content:"\f251"}.ion-toggle:before{content:"\f355"}.ion-toggle-filled:before{content:"\f354"}.ion-transgender:before{content:"\f4f5"}.ion-trash-a:before{content:"\f252"}.ion-trash-b:before{content:"\f253"}.ion-trophy:before{content:"\f356"}.ion-tshirt:before{content:"\f4f7"}.ion-tshirt-outline:before{content:"\f4f6"}.ion-umbrella:before{content:"\f2b7"}.ion-university:before{content:"\f357"}.ion-unlocked:before{content:"\f254"}.ion-upload:before{content:"\f255"}.ion-usb:before{content:"\f2b8"}.ion-videocamera:before{content:"\f256"}.ion-volume-high:before{content:"\f257"}.ion-volume-low:before{content:"\f258"}.ion-volume-medium:before{content:"\f259"}.ion-volume-mute:before{content:"\f25a"}.ion-wand:before{content:"\f358"}.ion-waterdrop:before{content:"\f25b"}.ion-wifi:before{content:"\f25c"}.ion-wineglass:before{content:"\f2b9"}.ion-woman:before{content:"\f25d"}.ion-wrench:before{content:"\f2ba"}.ion-xbox:before{content:"\f30c"}\r
diff --git a/LinuxGUI/WebUI/static/css/vtoy.css b/LinuxGUI/WebUI/static/css/vtoy.css
new file mode 100644 (file)
index 0000000..f9fcf4e
--- /dev/null
@@ -0,0 +1,357 @@
+/*loading ¶¯»­*/\r
+.loading {\r
+    width: 100%;\r
+    height: 100%;\r
+    position: fixed;\r
+    top:0;\r
+    left:0;\r
+    z-index: 999999;\r
+    background-color: rgba(0, 0, 0, 0);\r
+}\r
+.loading .title {\r
+    width: 300px;\r
+    text-align: center;\r
+    margin: 250px auto 0px;\r
+    color: #2F7095;\r
+    font-weight: 900;\r
+    font-size: 24px;\r
+}\r
+.loading .rectbox {\r
+    width: 150px;\r
+    height: 40px;\r
+    margin: 10px auto;\r
+}\r
+.loading .rectbox .title {\r
+    font-size: 14px;\r
+    font-family: 'Microsoft YaHei';\r
+    font-weight: 900;\r
+    text-align: center;\r
+    color: rgba(68, 149, 57, 1);\r
+}\r
+.loading .rectbox .rect {\r
+    width: 25px;\r
+    height: 25px;\r
+    background-color: rgba(68, 149, 57, 1);\r
+    margin: 0 2px auto 3px;\r
+    float: left;\r
+    -webkit-animation: loading 0.48s infinite ease-in-out;\r
+    -o-animation: loading 0.48s infinite ease-in-out;\r
+    animation: loading 0.48s infinite ease-in-out;\r
+}\r
+.loading .rectbox .rect1 {\r
+    -webkit-animation-delay: 0s;\r
+    -moz-animation-delay: 0s;\r
+    -o-animation-delay: 0s;\r
+    animation-delay: 0s;\r
+}\r
+.loading .rectbox .rect2 {\r
+    -webkit-animation-delay: 0.12s;\r
+    -moz-animation-delay: 0.12s;\r
+    -o-animation-delay: 0.12s;\r
+    animation-delay: 0.12s;\r
+    background-color: rgba(68, 149, 57, 0.2);\r
+}\r
+.loading .rectbox .rect3 {\r
+    -webkit-animation-delay: 0.24s;\r
+    -moz-animation-delay: 0.24s;\r
+    -o-animation-delay: 0.24s;\r
+    animation-delay: 0.24s;\r
+    background-color: rgba(68, 149, 57, 0.4);\r
+}\r
+.loading .rectbox .rect4 {\r
+    -webkit-animation-delay: 0.36s;\r
+    -moz-animation-delay: 0.36s;\r
+    -o-animation-delay: 0.36s;\r
+    animation-delay: 0.36s;\r
+    background-color: rgba(68, 149, 57, 0.6);\r
+}\r
+.loading .rectbox .rect5 {\r
+    -webkit-animation-delay: 0.48s;\r
+    -moz-animation-delay: 0.48s;\r
+    -o-animation-delay: 0.48s;\r
+    animation-delay: 0.48s;\r
+    background-color: rgba(68, 149, 57, 0.8);\r
+}\r
+@keyframes loading {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-moz-keyframes loading {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-ms-keyframes loading {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-webkit-keyframes loading {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+\r
+/*vtoy server is running*/\r
+#vtoy-main .loading {\r
+    width: 100%;\r
+    position: relative;\r
+    top:0;\r
+    left:0;\r
+    background-color: rgba(0, 0, 0, 0);\r
+}\r
+#vtoy-main .loading .title {\r
+    width: 300px;\r
+    text-align: center;\r
+    margin: 250px auto 0px;\r
+    color: #449539;\r
+    font-weight: 900;\r
+    font-size: 24px;\r
+}\r
+#vtoy-main .loading .rectbox {\r
+    width: 120px;\r
+    height: 40px;\r
+    margin: 10px auto;\r
+}\r
+#vtoy-main .loading .rectbox .title {\r
+    font-size: 14px;\r
+    font-family: 'Microsoft YaHei';\r
+    font-weight: 900;\r
+    text-align: center;\r
+    color: rgba(68, 149, 57, 1);\r
+}\r
+#vtoy-main .loading .rectbox .rect {\r
+    width: 25px;\r
+    height: 25px;\r
+    background-color: rgba(68, 149, 57, 1);\r
+    margin: 0 2px auto 3px;\r
+    float: left;\r
+    -webkit-animation: loading 1.44s infinite ease-in-out;\r
+    -o-animation: loading 0.48s infinite ease-in-out;\r
+    animation: loading 0.48s infinite ease-in-out;\r
+}\r
+#vtoy-main .loading .rectbox .rect1 {\r
+    -webkit-animation-delay: 0s;\r
+    -moz-animation-delay: 0s;\r
+    -o-animation-delay: 0s;\r
+    animation-delay: 0s;\r
+}\r
+#vtoy-main .loading .rectbox .rect2 {\r
+    -webkit-animation-delay: 0.36s;\r
+    -moz-animation-delay: 0.12s;\r
+    -o-animation-delay: 0.12s;\r
+    animation-delay: 0.12s;\r
+    background-color: rgba(68, 149, 57, 0.2);\r
+}\r
+#vtoy-main .loading .rectbox .rect3 {\r
+    -webkit-animation-delay: 0.72s;\r
+    -moz-animation-delay: 0.24s;\r
+    -o-animation-delay: 0.24s;\r
+    animation-delay: 0.24s;\r
+    background-color: rgba(68, 149, 57, 0.4);\r
+}\r
+#vtoy-main .loading .rectbox .rect4 {\r
+    -webkit-animation-delay: 1.08s;\r
+    -moz-animation-delay: 0.36s;\r
+    -o-animation-delay: 0.36s;\r
+    animation-delay: 0.36s;\r
+    background-color: rgba(68, 149, 57, 0.6);\r
+}\r
+#vtoy-main .loading .rectbox .rect5 {\r
+    -webkit-animation-delay: 1.44s;\r
+    -moz-animation-delay: 0.48s;\r
+    -o-animation-delay: 0.48s;\r
+    animation-delay: 0.48s;\r
+    background-color: rgba(68, 149, 57, 0.8);\r
+}\r
+@keyframes running {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-moz-keyframes running {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-ms-keyframes running {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+@-webkit-keyframes running {\r
+    0% {\r
+        background-color: rgba(68, 149, 57, 1);\r
+    }\r
+    25% {\r
+        background-color: rgba(68, 149, 57, 0.8);\r
+    }\r
+    50% {\r
+        background-color: rgba(68, 149, 57, 0.6);\r
+    }\r
+    75% {\r
+        background-color: rgba(68, 149, 57, 0.4);\r
+    }\r
+    100% {\r
+        background-color: rgba(68, 149, 57, 0.2);\r
+    }\r
+}\r
+\r
+\r
+\r
+.loadEffect{\r
+    width: 110px;\r
+    height: 110px;\r
+    position: relative;\r
+    margin: 0 auto;\r
+    margin-top:0 auto;\r
+}\r
+.loadEffect span{\r
+    display: inline-block;\r
+    width: 20px;\r
+    height: 20px;\r
+    border-radius: 50%;\r
+    background: lightgreen;\r
+    position: absolute;\r
+    -webkit-animation: load 1.04s ease infinite;\r
+}\r
+@-webkit-keyframes load{\r
+    0%{\r
+        opacity: 1;\r
+    }\r
+    100%{\r
+        opacity: 0.2;\r
+    }\r
+}\r
+\r
+\r
+\r
+\r
+.loadEffect span:nth-child(1){\r
+    left: 40%;\r
+    top: -130%;\r
+    -webkit-animation-delay:0.13s;\r
+}\r
+.loadEffect span:nth-child(2){\r
+    left: 90%;\r
+    top: 8%;\r
+    margin-top:-120%;\r
+    -webkit-animation-delay:0.26s;\r
+}\r
+.loadEffect span:nth-child(3){\r
+    left: 110%;\r
+    top: -80%;\r
+    margin-left: %-100;\r
+    -webkit-animation-delay:0.39s;\r
+}\r
+.loadEffect span:nth-child(4){\r
+    top: -40%;\r
+    left:110%;\r
+    -webkit-animation-delay:0.52s;\r
+}\r
+.loadEffect span:nth-child(5){\r
+    left: 40%;\r
+    top: 0;\r
+    margin-top:10%;\r
+    -webkit-animation-delay:0.65s;\r
+}\r
+.loadEffect span:nth-child(6){\r
+    left: -20%;\r
+    bottom:120%;\r
+    -webkit-animation-delay:0.78s;\r
+}\r
+.loadEffect span:nth-child(7){\r
+    bottom: 160%;\r
+    left: -20%;\r
+    -webkit-animation-delay:0.91s;\r
+}\r
+.loadEffect span:nth-child(8){\r
+    bottom: 200%;\r
+    left: -10%;\r
+    -webkit-animation-delay:1.04s;\r
+}\r
diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf
new file mode 100644 (file)
index 0000000..d7994e1
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf differ
diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff
new file mode 100644 (file)
index 0000000..6fd4ede
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff differ
diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2
new file mode 100644 (file)
index 0000000..5560193
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 differ
diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf
new file mode 100644 (file)
index 0000000..1413fc6
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff
new file mode 100644 (file)
index 0000000..9e61285
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff differ
diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2
new file mode 100644 (file)
index 0000000..64539b5
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/LinuxGUI/WebUI/static/fonts/ionicons.eot b/LinuxGUI/WebUI/static/fonts/ionicons.eot
new file mode 100644 (file)
index 0000000..9caa348
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/ionicons.eot differ
diff --git a/LinuxGUI/WebUI/static/fonts/ionicons.ttf b/LinuxGUI/WebUI/static/fonts/ionicons.ttf
new file mode 100644 (file)
index 0000000..180ce51
Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/ionicons.ttf differ
diff --git a/LinuxGUI/WebUI/static/img/dropdown.png b/LinuxGUI/WebUI/static/img/dropdown.png
new file mode 100644 (file)
index 0000000..201a4ca
Binary files /dev/null and b/LinuxGUI/WebUI/static/img/dropdown.png differ
diff --git a/LinuxGUI/WebUI/static/img/refresh.ico b/LinuxGUI/WebUI/static/img/refresh.ico
new file mode 100644 (file)
index 0000000..c2bc338
Binary files /dev/null and b/LinuxGUI/WebUI/static/img/refresh.ico differ
diff --git a/LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js b/LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js
new file mode 100644 (file)
index 0000000..49990d6
--- /dev/null
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function qa(){}qa.prototype=d.filters=d.pseudos,d.setFilters=new qa,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function ra(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){
+return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ia={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qa[0].contentDocument,b.write(),b.close(),c=sa(a,b),qa.detach()),ra[a]=c),c}var ua=/^margin/,va=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wa=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xa(a,b,c){var d,e,f,g,h=a.style;return c=c||wa(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),va.test(g)&&ua.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function ya(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var za=/^(none|table(?!-c[ea]).+)/,Aa=new RegExp("^("+Q+")(.*)$","i"),Ba=new RegExp("^([+-])=("+Q+")","i"),Ca={position:"absolute",visibility:"hidden",display:"block"},Da={letterSpacing:"0",fontWeight:"400"},Ea=["Webkit","O","Moz","ms"];function Fa(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Ea.length;while(e--)if(b=Ea[e]+c,b in a)return b;return d}function Ga(a,b,c){var d=Aa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Ha(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ia(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wa(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xa(a,b,f),(0>e||null==e)&&(e=a.style[b]),va.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Ha(a,b,c||(g?"border":"content"),d,f)+"px"}function Ja(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",ta(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xa(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fa(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Ba.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fa(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xa(a,b,d)),"normal"===e&&b in Da&&(e=Da[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?za.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Ca,function(){return Ia(a,b,d)}):Ia(a,b,d):void 0},set:function(a,c,d){var e=d&&wa(a);return Ga(a,c,d?Ha(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=ya(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xa,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ua.test(a)||(n.cssHooks[a+b].set=Ga)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wa(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Ja(this,!0)},hide:function(){return Ja(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Ka(a,b,c,d,e){return new Ka.prototype.init(a,b,c,d,e)}n.Tween=Ka,Ka.prototype={constructor:Ka,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Ka.propHooks[this.prop];return a&&a.get?a.get(this):Ka.propHooks._default.get(this)},run:function(a){var b,c=Ka.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ka.propHooks._default.set(this),this}},Ka.prototype.init.prototype=Ka.prototype,Ka.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Ka.propHooks.scrollTop=Ka.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Ka.prototype.init,n.fx.step={};var La,Ma,Na=/^(?:toggle|show|hide)$/,Oa=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pa=/queueHooks$/,Qa=[Va],Ra={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Oa.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Oa.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sa(){return setTimeout(function(){La=void 0}),La=n.now()}function Ta(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ua(a,b,c){for(var d,e=(Ra[b]||[]).concat(Ra["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Va(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||ta(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Na.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?ta(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ua(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wa(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xa(a,b,c){var d,e,f=0,g=Qa.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=La||Sa(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:La||Sa(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wa(k,j.opts.specialEasing);g>f;f++)if(d=Qa[f].call(j,a,k,j.opts))return d;return n.map(k,Ua,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xa,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Ra[c]=Ra[c]||[],Ra[c].unshift(b)},prefilter:function(a,b){b?Qa.unshift(a):Qa.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xa(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pa.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Ta(b,!0),a,d,e)}}),n.each({slideDown:Ta("show"),slideUp:Ta("hide"),slideToggle:Ta("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(La=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),La=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Ma||(Ma=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Ma),Ma=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Ya,Za,$a=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Za:Ya)),
+void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Za={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$a[b]||n.find.attr;$a[b]=function(a,b,d){var e,f;return d||(f=$a[b],$a[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$a[b]=f),e}});var _a=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_a.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ab=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ab," ").indexOf(b)>=0)return!0;return!1}});var bb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cb=n.now(),db=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var eb=/#.*$/,fb=/([?&])_=[^&]*/,gb=/^(.*?):[ \t]*([^\r\n]*)$/gm,hb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ib=/^(?:GET|HEAD)$/,jb=/^\/\//,kb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lb={},mb={},nb="*/".concat("*"),ob=a.location.href,pb=kb.exec(ob.toLowerCase())||[];function qb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rb(a,b,c,d){var e={},f=a===mb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function ub(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ob,type:"GET",isLocal:hb.test(pb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sb(sb(a,n.ajaxSettings),b):sb(n.ajaxSettings,a)},ajaxPrefilter:qb(lb),ajaxTransport:qb(mb),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gb.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||ob)+"").replace(eb,"").replace(jb,pb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kb.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pb[1]&&h[2]===pb[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pb[3]||("http:"===pb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rb(lb,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ib.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(db.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fb.test(d)?d.replace(fb,"$1_="+cb++):d+(db.test(d)?"&":"?")+"_="+cb++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nb+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rb(mb,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tb(k,v,f)),u=ub(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vb=/%20/g,wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&").replace(vb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bb=0,Cb={},Db={0:200,1223:204},Eb=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cb)Cb[a]()}),k.cors=!!Eb&&"withCredentials"in Eb,k.ajax=Eb=!!Eb,n.ajaxTransport(function(a){var b;return k.cors||Eb&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cb[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Db[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cb[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Fb=[],Gb=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Fb.pop()||n.expando+"_"+cb++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Gb.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Gb.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Gb,"$1"+e):b.jsonp!==!1&&(b.url+=(db.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Fb.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Hb=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Hb)return Hb.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Ib=a.document.documentElement;function Jb(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Jb(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Ib;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Ib})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Jb(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=ya(k.pixelPosition,function(a,c){return c?(c=xa(a,b),va.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Kb=a.jQuery,Lb=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Lb),b&&a.jQuery===n&&(a.jQuery=Kb),n},typeof b===U&&(a.jQuery=a.$=n),n});
diff --git a/LinuxGUI/WebUI/static/js/jquery.validate.min.js b/LinuxGUI/WebUI/static/js/jquery.validate.min.js
new file mode 100644 (file)
index 0000000..36d91fd
--- /dev/null
@@ -0,0 +1,4 @@
+/*! jQuery Validation Plugin - v1.14.0 - 6/30/2015
+ * http://jqueryvalidation.org/
+ * Copyright (c) 2015 Jörn Zaefferer; Licensed MIT */
+!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.settings.submitHandler&&(c.submitButton=b.target),a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.settings.submitHandler?(c.submitButton&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),e=c.settings.submitHandler.call(c,c.currentForm,b),c.submitButton&&d.remove(),void 0!==e?e:!1):!0}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,d=d.concat(c.errorList)}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(b,c){i[c]=f[c],delete f[c],"required"===c&&a(j).removeAttr("aria-required")}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g),a(j).attr("aria-required","true")),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}),a.extend(a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){return!!a.trim(""+a(b).val())},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||-1!==a.inArray(c.keyCode,d)||(b.name in this.submitted||b===this.lastElement)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date ( ISO ).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler),a(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required","true")},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c=this.clean(b),d=this.validationTargetFor(c),e=!0;return this.lastElement=d,void 0===d?delete this.invalid[c.name]:(this.prepareElement(d),this.currentElements=a(d),e=this.check(d)!==!1,e?delete this.invalid[d.name]:this.invalid[d.name]=!0),a(b).attr("aria-invalid",!e),this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),e},showErrors:function(b){if(b){a.extend(this.errorMap,b),this.errorList=[];for(var c in b)this.errorList.push({message:b[c],element:this.findByName(c)[0]});this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors();var b,c=this.elements().removeData("previousValue").removeAttr("aria-invalid");if(this.settings.unhighlight)for(b=0;c[b];b++)this.settings.unhighlight.call(this,c[b],this.settings.errorClass,"");else c.removeClass(this.settings.errorClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){return!this.name&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in c||!b.objectLength(a(this).rules())?!1:(c[this.name]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([]),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d=a(b),e=b.type;return"radio"===e||"checkbox"===e?this.findByName(b.name).filter(":checked").val():"number"===e&&"undefined"!=typeof b.validity?b.validity.badInput?!1:d.val():(c=d.val(),"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f=a(b).rules(),g=a.map(f,function(a,b){return b}).length,h=!1,i=this.elementValue(b);for(d in f){e={method:d,parameters:f[d]};try{if(c=a.validator.methods[d].call(this,i,b,e.parameters),"dependency-mismatch"===c&&1===g){h=!0;continue}if(h=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(j){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",j),j instanceof TypeError&&(j.message+=".  Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),j}}if(!h)return this.objectLength(f)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a];return void 0},defaultMessage:function(b,c){return this.findDefined(this.customMessage(b.name,c),this.customDataMessage(b,c),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c],"<strong>Warning: No message defined for "+b.name+"</strong>")},formatAndAdd:function(b,c){var d=this.defaultMessage(b,c.method),e=/\$?\{(\d+)\}/g;"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),this.errorList.push({message:d,element:b,method:c.method}),this.errorMap[b.name]=d,this.submitted[b.name]=d},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g=this.errorsFor(b),h=this.idOrName(b),i=a(b).attr("aria-describedby");g.length?(g.removeClass(this.settings.validClass).addClass(this.settings.errorClass),g.html(c)):(g=a("<"+this.settings.errorElement+">").attr("id",h+"-error").addClass(this.settings.errorClass).html(c||""),d=g,this.settings.wrapper&&(d=g.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement(d,a(b)):d.insertAfter(b),g.is("label")?g.attr("for",h):0===g.parents("label[for='"+h+"']").length&&(f=g.attr("id").replace(/(:|\.|\[|\]|\$)/g,"\\$1"),i?i.match(new RegExp("\\b"+f+"\\b"))||(i+=" "+f):i=f,a(b).attr("aria-describedby",i),e=this.groups[b.name],e&&a.each(this.groups,function(b,c){c===e&&a("[name='"+b+"']",this.currentForm).attr("aria-describedby",g.attr("id"))}))),!c&&this.settings.success&&(g.text(""),"string"==typeof this.settings.success?g.addClass(this.settings.success):this.settings.success(g,b)),this.toShow=this.toShow.add(g)},errorsFor:function(b){var c=this.idOrName(b),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+d.replace(/\s+/g,", #")),this.errors().filter(e)},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+b+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return this.dependTypes[typeof a]?this.dependTypes[typeof a](a,b):!0},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(a){this.pending[a.name]||(this.pendingRequest++,this.pending[a.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b){return a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,"remote")})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0!==e.param?e.param:!0:delete b[d]}}),a.each(b,function(d,e){b[d]=a.isFunction(e)?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},creditcard:function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||d>=e},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||c>=a},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.off(".validate-equalTo").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d){if(this.optional(c))return"dependency-mismatch";var e,f,g=this.previousValue(c);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),g.originalMessage=this.settings.messages[c.name].remote,this.settings.messages[c.name].remote=g.message,d="string"==typeof d&&{url:d}||d,g.old===b?g.valid:(g.old=b,e=this,this.startRequest(c),f={},f[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:f,context:e.currentForm,success:function(d){var f,h,i,j=d===!0||"true"===d;e.settings.messages[c.name].remote=g.originalMessage,j?(i=e.formSubmitted,e.prepareElement(c),e.formSubmitted=i,e.successList.push(c),delete e.invalid[c.name],e.showErrors()):(f={},h=d||e.defaultMessage(c,"remote"),f[c.name]=g.message=a.isFunction(h)?h(b):h,e.invalid[c.name]=!0,e.showErrors(f)),g.valid=j,e.stopRequest(c,j)}},d)),"pending")}}});var b,c={};a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)})});
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/js/jquery.vtoy.alert.js b/LinuxGUI/WebUI/static/js/jquery.vtoy.alert.js
new file mode 100644 (file)
index 0000000..c59f8b4
--- /dev/null
@@ -0,0 +1,223 @@
+\r
+/**\r
+ * 用于bootstap框架下提示信息框\r
+ * demo: \r
+ * \r
+ * 模态框\r
+ * Modal.confirm({msg: "是否确定提交?"}).on( function (e) {alert("返回结果:" + e);});\r
+ * Modal.alert({msg:"该记录已删除!"})\r
+ * Modal.process('show'/'hide') 隐藏或显示全屏、进度条\r
+ * \r
+ * 非模态框\r
+ * Message.show({ type : 'S|W|E|I', msg: '提示信息' })\r
+ * Message.success('成功信息')\r
+ * Message.error('错误信息')\r
+ * Message.warn('警告信息')\r
+ * Message.info('提示信息')\r
+ * Message.warn('警告信息',10000) //10000为显示时长\r
+ */\r
+;$(function() {\r
+    \r
+       window.Modal = function() {\r
+               var reg = new RegExp("\\[([^\\[\\]]*?)\\]", 'igm');\r
+               var alr = $("#msgAlertDiv");\r
+        \r
+        if (alr.length == 0) {\r
+            alr = $('<div id="msgAlertDiv" class="modal fade"></div>')\r
+            $("body").append(alr);\r
+        }\r
+    \r
+               var ahtml = ' <div class="modal-dialog">'\r
+            + '<div class="modal-content">'\r
+            + '<div class="modal-header">'\r
+            + '<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>'\r
+            + '<h3 class="modal-title"><B style="font-weight: 400;"> [Title]</B></h3>'\r
+            + '</div>'\r
+            + '<div class="modal-body " style="word-break: break-all;display: block;">'\r
+            + '[Message]'\r
+            + '</div>'\r
+            + '<div class="modal-footer" >'\r
+            + '<button type="button" class="btn btn-success ok" data-dismiss="modal">[BtnOk]</button>'\r
+            + '[UserDefined]'\r
+            + '<button type="button" class="btn cancel" data-dismiss="modal">[BtnCancel]</button>'\r
+            + '</div>' + '</div>' + '</div>';\r
+\r
+               var _alert = function(options) {\r
+                       alr.html(ahtml); // 复原\r
+                       alr.find('.ok').removeClass('btn-success').addClass('btn-primary');\r
+            \r
+            if (vtoy_chrome_app_mode == 0) {\r
+                if (document.body.clientHeight > 400) {\r
+                    alr.find('.modal-dialog').css("top",((document.body.clientHeight - 400) / 2)); \r
+                }\r
+            }\r
+            \r
+                       alr.find('.cancel').hide();\r
+                       _dialog(options);\r
+\r
+                       return {\r
+                               on : function(callback) {\r
+                                       if (callback && callback instanceof Function) {\r
+                                               alr.find('.ok').click(function() {\r
+                                                       callback(true)\r
+                                               });\r
+                                       }\r
+                               }\r
+                       };\r
+               };\r
+\r
+               var _confirm = function(options) {\r
+                       alr.html(ahtml);\r
+                       alr.find('.ok').removeClass('btn-primary').addClass('btn-success');\r
+                       alr.find('.cancel').show();\r
+            if (vtoy_chrome_app_mode == 0) {\r
+                if (document.body.clientHeight > 400) {\r
+                    alr.find('.modal-dialog').css("top",((document.body.clientHeight - 400) / 2)); \r
+                }\r
+            }\r
+            \r
+                       _dialog(options);\r
+\r
+                       return {\r
+                               on : function(callback) {\r
+                                       if (callback && callback instanceof Function) {\r
+                                               alr.find('.ok').click(function() {\r
+                                                       callback(true);\r
+                                               });\r
+                                               alr.find('.cancel').click(function() {\r
+                                                       callback(false);\r
+                                               });\r
+                                               alr.find('.userDefiend').click(function() {\r
+                            callback("userDefiend");\r
+                        });\r
+                                       }\r
+                               }\r
+                       };\r
+               };\r
+\r
+               var _dialog = function(options) {\r
+                       var ops = {\r
+                               msg : "提示内容",\r
+                               title : "操作提示",\r
+                               btnok : "确定",\r
+                               btncl : "取消",\r
+                               userDefined : ""\r
+                       };\r
+\r
+                       $.extend(ops, options);\r
+\r
+                       var html = alr.html().replace(reg, function(node, key) {\r
+                               return {\r
+                                       Title : ops.title,\r
+                                       Message : ops.msg,\r
+                                       BtnOk : ops.btnok,\r
+                                       BtnCancel : ops.btncl,\r
+                                       UserDefined : ops.userDefined\r
+                               }[key];\r
+                       });\r
+\r
+                       alr.html(html);\r
+                       alr.modal({\r
+                               width : 500,\r
+                               backdrop : 'static'\r
+                       });\r
+               }\r
+\r
+               var _process = function(showOrHide,time) {\r
+                   var defaultTime = 100;\r
+                   if($.isNumeric(time)) {\r
+                       defaultTime = time;\r
+                   }\r
+                   var $proc;\r
+            $proc = $("#vtoy_proc");\r
+            if('hide' === showOrHide) {\r
+                $proc.remove();\r
+            } else if ('show' === showOrHide) {\r
+                if($proc.length == 1) {\r
+                    return;\r
+                }\r
+                $(document).find(":focus").blur();\r
+                $proc = $('<div id="vtoy_proc" class="loading"></div>');\r
+                $("body").append($proc);\r
+                setTimeout(function() {\r
+                    $proc.replaceWith('<div id="vtoy_proc" class="loading" style="background-color: rgba(0, 0, 0, 0.2);"><div class="rectbox"><div class="title">DATA</div><div class="rect rect1"></div><div class="rect rect2"></div><div class="rect rect3"></div><div class="rect rect4"></div><div class="rect rect5"></div></div></div>');\r
+                }, $.isNumeric(time) ? time : 100);\r
+            } else {\r
+                alert("Modal.process参数必须为show/hide");\r
+            }\r
+               }\r
+               \r
+               return {\r
+                       alert : _alert,\r
+                       confirm : _confirm,\r
+                       process : _process\r
+               }\r
+\r
+       }();\r
+       \r
+       window.Message = function() {\r
+           var _showMsg = function(type, msg, time) {\r
+               var o = {type : type, msg : msg };\r
+               if(time) {\r
+                   o.time = time;\r
+               }\r
+               _show(o);\r
+           }\r
+               \r
+               var _show = function(options) {\r
+                       var ops = {\r
+                               msg : "提示内容",\r
+                               type: 'S',\r
+                               time: 3000\r
+                       };\r
+                       $.extend(ops, options);\r
+\r
+                       var msg_class = 'alert-success';\r
+                       if('S' === ops.type || 's' === ops.type) {\r
+                               msg_class = 'alert-success';\r
+                       } else if ('E' === ops.type || 'e' === ops.type) {\r
+                               msg_class = 'alert-danger';\r
+                       } else if ('W' === ops.type || 'w' === ops.type) {\r
+                               msg_class = 'alert-warning';\r
+                       } else if ('I' === ops.type || 'i' === ops.type) {\r
+                           msg_class = 'alert-info';\r
+                       } else {\r
+                               alert("未知的类型,请使用: w-警告;s-成功;e-失败;i-提示");\r
+                               return;\r
+                       }\r
+                       var $messageContainer = $("#fcss_message");\r
+                       if($messageContainer.length === 0) {\r
+                               $messageContainer = $('<div id="fcss_message" style="position:fixed; left: 20%; right: 20%; top:0px; z-index:99999999"></div>');\r
+                               $messageContainer.appendTo($('body'));\r
+                       }\r
+                       var $div = $('<div class="alert ' + msg_class + ' alert-dismissible fade in" role="alert" style="margin-bottom: 0; padding-top:10px; padding-bottom: 10px;"></div>');\r
+                       var $btn = $('<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>');\r
+                       $div.append($btn).append(ops.msg).appendTo($messageContainer);\r
+                       setTimeout(function() {\r
+                               $div.remove();\r
+                       }, ops.time);\r
+               }\r
+               \r
+               var _success = function(msg, time) {\r
+                   _showMsg('s', msg, time);\r
+               }\r
+               var _error = function(msg, time) {\r
+                   _showMsg('e', msg, time || 6000);\r
+               }\r
+               var _warn = function(msg, time) {\r
+                   _showMsg('w', msg, time);\r
+               }\r
+               var _info = function(msg, time) {\r
+                   _showMsg('i', msg, time);\r
+               }\r
+               \r
+               return {\r
+                       success : _success,\r
+                       error   : _error,\r
+                       warn    : _warn,\r
+                       info    : _info,\r
+                       show    : _show\r
+               }\r
+       }();\r
+       \r
+});
\ No newline at end of file
diff --git a/LinuxGUI/WebUI/static/js/vtoy.js b/LinuxGUI/WebUI/static/js/vtoy.js
new file mode 100644 (file)
index 0000000..6423643
--- /dev/null
@@ -0,0 +1,279 @@
+\r
+// 包装ajax请求\r
+function callVtoy(p1, p2, p3) {\r
+    var url = '/vtoy/json';\r
+    var data = {};\r
+    var func = function(data) {};\r
+\r
+    if (typeof(p1) === 'string') {\r
+        url = p1;\r
+    } else if (typeof(p1) === 'object') {\r
+        data = p1;\r
+    }\r
+    if (typeof(p2) === 'object') {\r
+        data = p2;\r
+    } else if (typeof(p2) === 'function') {\r
+        func = p2;\r
+    }\r
+    if (typeof(p3) === 'function') {\r
+        func = p3;\r
+    }\r
+\r
+    //vtoy.debug('callVtoy:\t\t\t\t' + JSON.stringify(data));\r
+    $.ajax({\r
+        url: url,\r
+        type: 'post',\r
+        cache: false,\r
+        dataType: 'json',\r
+        data: JSON.stringify(data),\r
+        success: func,\r
+        error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+\r
+            if(undefined === errorThrown)\r
+            {\r
+                Message.error(vtoy_cur_language.STR_WEB_REMOTE_ABNORMAL);\r
+            }\r
+            else if(undefined === errorThrown.length)\r
+            {\r
+                \r
+            }\r
+            else if('' == errorThrown.trim())\r
+            {\r
+            }\r
+            else\r
+            {\r
+                switch(errorThrown)\r
+                {\r
+                    case 'timeout':\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_REQUEST_TIMEOUT);\r
+                        break;\r
+                    }\r
+                    case 'Service Unavailable':\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_SERVICE_UNAVAILABLE);\r
+                        break;\r
+                    }\r
+                    case 'abort':\r
+                    {\r
+                        break;\r
+                    }\r
+                    default:\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_COMMUNICATION_ERR + errorThrown);\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+        },\r
+        complete: function(data) {\r
+        //vtoy.debug('callVtoy\'s resp:\t\t' + data.responseText);\r
+        }\r
+    });\r
+}\r
+\r
+function callVtoyASyncTimeout(time, data, func) {\r
+    $.ajax({\r
+        url: '/vtoy/json',\r
+        type: 'post',\r
+        cache: false,\r
+        dataType: 'json',\r
+        async: true,\r
+        timeout: time,\r
+        data: JSON.stringify(data),\r
+        success: func,\r
+        error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+            if(undefined === errorThrown)\r
+            {                \r
+            }\r
+            else if(undefined === errorThrown.length)\r
+            {\r
+                \r
+            }\r
+            else if('' == errorThrown.trim())\r
+            {\r
+            }\r
+            else\r
+            {\r
+                switch(errorThrown)\r
+                {\r
+                    case 'timeout':\r
+                    {\r
+                        callVtoyASyncTimeout(time, data, func);\r
+                        break;\r
+                    }\r
+                    case 'Service Unavailable':\r
+                    {\r
+                        break;\r
+                    }\r
+                    case 'abort':\r
+                    {\r
+                        break;\r
+                    }\r
+                    default:\r
+                    {\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+        },\r
+        complete: function(data) {\r
+            //vtoy.debug('callVtoyASyncTimeout\'s resp:\t' + JSON.stringify(data));\r
+        }\r
+    });\r
+}\r
+\r
+function callVtoySync(data, func) {\r
+    //vtoy.debug('callVtoySync:\t\t\t' + JSON.stringify(data));\r
+    $.ajax({\r
+        url: '/vtoy/json',\r
+        type: 'post',\r
+        cache: false,\r
+        dataType: 'json',\r
+        async: false,\r
+        data: JSON.stringify(data),\r
+        success: function VtoyCallFuncWrapper(data) {            \r
+            if (data.result === 'tokenerror') {\r
+                var titlestr = '<span class="fa  fa-minus-circle" style="color:#dd4b39; font-weight:bold;"> ' + vtoy_cur_language.STR_ERROR + '</span>';\r
+                var msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_WEB_TOKEN_MISMATCH + '</span>';                    \r
+                \r
+                Modal.alert({title:titlestr, msg:msgstr, btnok:vtoy_cur_language.STR_BTN_OK }).on(function(e) {\r
+                    window.location.reload(true);\r
+                });\r
+            } \r
+            else if (data.result === 'busy') {\r
+                var titlestr = '<span class="fa fa-check-circle" style="color:green; font-weight:bold;"> ' + vtoy_cur_language.STR_INFO + '</span>';\r
+                var msgstr = '<span style="font-size:14px; font-weight:bold;"> ' + vtoy_cur_language.STR_WEB_SERVICE_BUSY + '</span>';\r
+                Modal.alert({title:titlestr, msg:msgstr, btnok:vtoy_cur_language.STR_BTN_OK });                \r
+            }else {\r
+                func(data);\r
+            }\r
+        },\r
+        error: function(xmlHttpRequest, textStatus, errorThrown) {\r
+            if(undefined === errorThrown)\r
+            {\r
+                Message.error(vtoy_cur_language.STR_WEB_REMOTE_ABNORMAL);\r
+            }\r
+            else if(undefined === errorThrown.length)\r
+            {\r
+                \r
+            }\r
+            else if('' == errorThrown.trim())\r
+            {\r
+            }\r
+            else\r
+            {\r
+                switch(errorThrown)\r
+                {\r
+                    case 'timeout':\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_REQUEST_TIMEOUT);\r
+                        break;\r
+                    }\r
+                    case 'Service Unavailable':\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_SERVICE_UNAVAILABLE);\r
+                        break;\r
+                    }\r
+                    case 'abort':\r
+                    {\r
+                        break;\r
+                    }\r
+                    default:\r
+                    {\r
+                        Message.error(vtoy_cur_language.STR_WEB_COMMUNICATION_ERR + errorThrown);\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+        },\r
+        complete: function(data) {\r
+            //vtoy.debug('callVtoySync\'s resp:\t' + JSON.stringify(data));\r
+        }\r
+    });\r
+}\r
+\r
+var vtoy = {    \r
+    baseurl : '',\r
+    status: '',    \r
+    scan: {\r
+        time: 3,\r
+        ret: []\r
+    }\r
+}\r
+\r
+// \r
+String.prototype.endsWith = function(str) {\r
+    if (str == null || str == "" || this.length == 0 || str.length > this.length)\r
+        return false;\r
+    if (this.substring(this.length - str.length) == str)\r
+        return true;\r
+    else\r
+        return false;\r
+}\r
+\r
+window.Message = function() {\r
+    var _showMsg = function(type, msg, time) {\r
+        var o = {type : type, msg : msg };\r
+        if(time) {\r
+            o.time = time;\r
+        }\r
+        _show(o);\r
+    }\r
+    \r
+    var _show = function(options) {\r
+        var ops = {\r
+            msg : "提示内容",\r
+            type: 'S',\r
+            time: 3000\r
+        };\r
+        $.extend(ops, options);\r
+\r
+        var msg_class = 'alert-success';\r
+        if('S' === ops.type || 's' === ops.type) {\r
+            msg_class = 'alert-success';\r
+        } else if ('E' === ops.type || 'e' === ops.type) {\r
+            msg_class = 'alert-danger';\r
+        } else if ('W' === ops.type || 'w' === ops.type) {\r
+            msg_class = 'alert-warning';\r
+        } else if ('I' === ops.type || 'i' === ops.type) {\r
+            msg_class = 'alert-info';\r
+        } else {\r
+            alert("未知的类型,请使用: w-警告;s-成功;e-失败;i-提示");\r
+            return;\r
+        }\r
+        var $messageContainer = $("#fcss_message");\r
+        if($messageContainer.length === 0) {\r
+            $messageContainer = $('<div id="fcss_message" style="position:fixed; left: 20%; right: 20%; top:0px; z-index:99999999"></div>');\r
+            $messageContainer.appendTo($('body'));\r
+        }\r
+        var $div = $('<div class="alert ' + msg_class + ' alert-dismissible fade in" role="alert" style="margin-bottom: 0; padding-top:10px; padding-bottom: 10px;"></div>');\r
+        var $btn = $('<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>');\r
+        $div.append($btn).append(ops.msg).appendTo($messageContainer);\r
+        setTimeout(function() {\r
+            $div.remove();\r
+        }, ops.time);\r
+    }\r
+    \r
+    var _success = function(msg, time) {\r
+        _showMsg('s', msg, time);\r
+    }\r
+    var _error = function(msg, time) {        \r
+        _showMsg('e', msg, time || 5000);\r
+    }\r
+    var _warn = function(msg, time) {\r
+        _showMsg('w', msg, time);\r
+    }\r
+    var _info = function(msg, time) {\r
+        _showMsg('i', msg, time);\r
+    }\r
+    \r
+    return {\r
+        success : _success,\r
+        error  : _error,\r
+        warn   : _warn,\r
+        info    : _info,\r
+        show   : _show\r
+    }\r
+}();\r
+\r
diff --git a/LinuxGUI/build.sh b/LinuxGUI/build.sh
new file mode 100644 (file)
index 0000000..21830ac
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+build_func() {    
+    libsuffix=$2
+    toolDir=$3
+    
+    XXFLAG='-std=gnu99 -D_FILE_OFFSET_BITS=64'
+    XXLIB="./Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_${libsuffix}.a"
+    
+    echo "CC=$1 libsuffix=$libsuffix"    
+    
+    $1 $XXFLAG -c -Wall -Wextra -Wshadow -Wformat-security -Winit-self \
+        -Wmissing-prototypes -O2 -DLINUX \
+        -I./Ventoy2Disk/Lib/libhttp/include \
+        -DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED \
+        -DUSE_STACK_SIZE=102400 -DNDEBUG -fPIC \
+        ./Ventoy2Disk/Lib/libhttp/include/civetweb.c \
+        -o ./civetweb.o
+    
+    $1 $XXFLAG -O2 -Wall -Wno-unused-function -DSTATIC=static -DINIT= \
+        -I./Ventoy2Disk \
+        -I./Ventoy2Disk/Core \
+        -I./Ventoy2Disk/Web \
+        -I./Ventoy2Disk/Include \
+        -I./Ventoy2Disk/Lib/libhttp/include \
+        -I./Ventoy2Disk/Lib/fat_io_lib/include \
+        -I./Ventoy2Disk/Lib/xz-embedded/linux/include \
+        -I./Ventoy2Disk/Lib/xz-embedded/linux/include/linux \
+        -I./Ventoy2Disk/Lib/xz-embedded/userspace \
+        -I ./Ventoy2Disk/Lib/exfat/src/libexfat \
+        -I ./Ventoy2Disk/Lib/exfat/src/mkfs \
+        \
+        -L ./Ventoy2Disk/Lib/fat_io_lib/lib \
+        Ventoy2Disk/*.c \
+        Ventoy2Disk/Core/*.c \
+        Ventoy2Disk/Web/*.c \
+        Ventoy2Disk/Lib/xz-embedded/linux/lib/decompress_unxz.c \
+        Ventoy2Disk/Lib/exfat/src/libexfat/*.c \
+        Ventoy2Disk/Lib/exfat/src/mkfs/*.c \
+        $XXLIB \
+        -l pthread \
+        ./civetweb.o \
+        -o V2D$libsuffix
+
+    rm -f *.o
+    
+    if [ "$libsuffix" = "aa64" ]; then
+        aarch64-linux-gnu-strip V2D$libsuffix
+    else
+        strip V2D$libsuffix
+    fi
+    
+    rm -f ../INSTALL/tool/$toolDir/V2DServer
+    cp -a V2D$libsuffix ../INSTALL/tool/$toolDir/V2DServer
+}
+
+build_func "gcc" '64' 'x86_64'
+build_func "gcc -m32" '32' 'i386'
+build_func "aarch64-linux-gnu-gcc" 'aa64' 'aarch64'
+
+
diff --git a/LinuxGUI/language.sh b/LinuxGUI/language.sh
new file mode 100644 (file)
index 0000000..2a0b6df
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+echo "generating languages.js ..."
+
+iconv -f utf-16 -t utf-8 ../LANGUAGES/languages.ini  | egrep -v '=STR|^;' | egrep  'Language-|STR_' > languages.js
+
+dos2unix languages.js
+
+sed 's/\(STR_.*\)=/"\1":/g' -i languages.js
+
+sed "s/: *'/:\"/g" -i languages.js
+
+sed "s/'\s*$/\",/g" -i languages.js
+
+sed 's/\[Language-\(.*\)\].*/"STR_XXX":""},{"name":"\1",/g' -i languages.js
+
+sed "1s/.*\},/var vtoy_language_data = \[/" -i languages.js
+
+sed 's/\("STR_WEB_COMMUNICATION_ERR"[^,]*\)/\1,/g' -i languages.js
+sed 's/,,/,/g' -i languages.js
+
+CNT=$(grep -v -c ',$' languages.js)
+
+if [ $CNT -gt 0 ]; then
+    echo "====== FAILED ========="
+    grep -v -n ',$' languages.js
+    exit 1
+fi
+
+
+echo '"STR_XXX":""}' >> languages.js
+echo '];' >> languages.js
+
+rm -f WebUI/static/js/languages.js
+mv languages.js WebUI/static/js/
+
+echo "====== SUCCESS =========="
\ No newline at end of file