diff --git a/source_codes/stardictd/ChangeLog b/source_codes/stardictd/ChangeLog
index 401b48a..ab95690 100644
--- a/source_codes/stardictd/ChangeLog
+++ b/source_codes/stardictd/ChangeLog
@@ -1,4 +1,4 @@
-2021.10.
+2021.12.
 stardictd-0.5.3 released.
 Protocol add RSA encrypt support!
 
diff --git a/source_codes/stardictd/configure.ac b/source_codes/stardictd/configure.ac
index 42e2088..ed008d0 100644
--- a/source_codes/stardictd/configure.ac
+++ b/source_codes/stardictd/configure.ac
@@ -4,7 +4,7 @@
 AC_PREREQ(2.59)
 AC_INIT(stardictd, 0.5.3, http://stardictd.sourceforge.net)
 AC_CONFIG_SRCDIR([src/main.cpp])
-AM_INIT_AUTOMAKE([dist-xz])
+AM_INIT_AUTOMAKE([tar-ustar dist-xz])
 AC_CONFIG_HEADER([config.h])
 
 LT_INIT
diff --git a/source_codes/stardictd/doc/StarDict-Protocol_v04 b/source_codes/stardictd/doc/StarDict-Protocol_v04
index 687f433..6e3834e 100644
--- a/source_codes/stardictd/doc/StarDict-Protocol_v04
+++ b/source_codes/stardictd/doc/StarDict-Protocol_v04
@@ -259,6 +259,11 @@ The adinfo XML String will be returned.
 Here is an example:
 <title>Upgrade Now!</title><url>http://www.stardict.org/finance.php</url>
 
+2.30 The FUZZYQUERY Command
+Command "fuzzyquery selected_word"
+Responses: "CODE_OK", "CODE_DICTMASK_NOTSET".
+If the response is ""CODE_OK", the FloatWin DictResult will be followed.
+
 
 3. More information
 
@@ -278,4 +283,4 @@ If you have any questions, email me. :)
 
 Hu Zheng <huzheng001@gmail.com>
 http://www.huzheng.org
-2020.11.12
+2021.12.14
diff --git a/source_codes/stardictd/src/daemon.cpp b/source_codes/stardictd/src/daemon.cpp
index f4bbd6b..25341ac 100644
--- a/source_codes/stardictd/src/daemon.cpp
+++ b/source_codes/stardictd/src/daemon.cpp
@@ -812,6 +812,16 @@ static void daemon_selectquery(std::string &word)
 	stardictdMain.selectquery(word.c_str());
 }
 
+static void daemon_fuzzyquery(std::string &word)
+{
+	if (check_dictmask()) {
+		daemon_printf( "%d\n", CODE_DICTMASK_NOTSET );
+		return;
+	}
+	daemon_printf( "%d\n", CODE_OK );
+	stardictdMain.fuzzyquery(word.c_str());
+}
+
 static void daemon_smartquery(std::string &word, std::string &BeginPos)
 {
 	if (check_dictmask()) {
@@ -976,6 +986,8 @@ static int _handleconn (int delay_time, int max_cost_time, int error)
 			daemon_query((*args)[1]);
 		} else if ((*args)[0] == "selectquery" && args->size()>=2) {
 			daemon_selectquery((*args)[1]);
+		} else if ((*args)[0] == "fuzzyquery" && args->size()>=2) {
+			daemon_fuzzyquery((*args)[1]);
 		} else if ((*args)[0] == "smartquery" && args->size()>=3) {
 			daemon_smartquery((*args)[1], (*args)[2]);
 		} else if ((*args)[0] == "define" && args->size()>=2) {
diff --git a/source_codes/stardictd/src/lib/stddict.cpp b/source_codes/stardictd/src/lib/stddict.cpp
index 5d89520..8519aa3 100644
--- a/source_codes/stardictd/src/lib/stddict.cpp
+++ b/source_codes/stardictd/src/lib/stddict.cpp
@@ -119,17 +119,16 @@ static inline bool bIsVowel(gchar inputchar)
   return( ch=='A' || ch=='E' || ch=='I' || ch=='O' || ch=='U' );
 }
 
+typedef unsigned char   uchar;  /* Short for unsigned char */
 
-bool bIsPureEnglish(const gchar *str)
+bool IsASCII(const char *str)
 {
-  // i think this should work even when it is UTF8 string :).
-  for (int i=0; str[i]!=0; i++)
-    //if(str[i]<0)
-    //if(str[i]<32 || str[i]>126) // tab equal 9,so this is not OK.
-    // Better use isascii() but not str[i]<0 while char is default unsigned in arm
-    if (!isascii(str[i]))
-            return false;
-  return true;
+	// works with UTF-8 strings (bytes of a multi-byte character has the highest 
+	// bit set)
+	for(; *str; ++str)
+		if(uchar(*str) >= 128)
+			return false;
+	return true;
 }
 
 class offset_index : public index_file {
diff --git a/source_codes/stardictd/src/stardictdmain.cpp b/source_codes/stardictd/src/stardictdmain.cpp
index f17d50d..df377e5 100644
--- a/source_codes/stardictd/src/stardictdmain.cpp
+++ b/source_codes/stardictd/src/stardictdmain.cpp
@@ -634,7 +634,61 @@ void StardictdMain::define(const gchar* sWord)
 
 void StardictdMain::LookupWithFuzzyToFloatWin(const gchar *sWord)
 {
-	
+	gchar *fuzzy_reslist[MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM];
+	bool Found = oLibs.LookupWithFuzzy(sWord, fuzzy_reslist, MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM, scan_dictmask);
+	if (Found) {
+		net_write_str("g");
+		int i, count=0;
+		for (i=0; i<MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM; i++) {
+			if (fuzzy_reslist[i])
+				count++;
+			else
+				break;
+		}
+		gchar ****ppppWord = (gchar ****)g_malloc(sizeof(gchar ***) * count);
+		gchar *****pppppWordData = (gchar *****)g_malloc(sizeof(gchar ****) * count);
+		const gchar **ppOriginWord = (const gchar **)g_malloc(sizeof(gchar *) * count);
+		CurrentIndex *iIndex = (CurrentIndex *)g_malloc(sizeof(CurrentIndex) * scan_dictmask.size());
+
+		gchar ***pppWord;
+		gchar ****ppppWordData;
+		for (i=0;i<count;i++) {
+			net_write_str("d");
+			bool bFound = false;
+			pppWord = (gchar ***)g_malloc(sizeof(gchar **) * scan_dictmask.size());
+			ppppWordData = (gchar ****)g_malloc(sizeof(gchar ***) * scan_dictmask.size());
+
+			ppOriginWord[i] = fuzzy_reslist[i];
+			for (size_t iLib=0; iLib<scan_dictmask.size(); iLib++)
+				BuildResultData(scan_dictmask, fuzzy_reslist[i], iIndex, NULL, iLib, pppWord, ppppWordData, bFound, 2);
+			//for (size_t iLib=0; iLib<scan_dictmask.size(); iLib++)
+				//BuildVirtualDictData(scan_dictmask, fuzzy_reslist[i], iLib, pppWord, ppppWordData, bFound);
+			if (bFound) {// it is certainly be true.
+				ppppWord[i]=pppWord;
+				pppppWordData[i]=ppppWordData;
+			} else {
+				FreeResultData(scan_dictmask.size(), pppWord, ppppWordData);
+				ppppWord[i]=NULL;
+			}
+			net_write_str("");
+		}
+		oFloatWin.AppendTextFuzzy(ppppWord, pppppWordData, ppOriginWord, count, sWord);
+		for (i=0; i<count; i++) {
+			if (ppppWord[i])
+				FreeResultData(scan_dictmask.size(), ppppWord[i], pppppWordData[i]);
+		}
+		g_free(ppppWord);
+		g_free(pppppWordData);
+		g_free(ppOriginWord);
+		g_free(iIndex);
+
+		for (i=0;i<count;i++)
+			g_free(fuzzy_reslist[i]);
+	} else {
+		ShowNotFound(sWord, REASON_NOT_FOUND);
+		net_write_str("h");
+	}
+	net_write_str("");
 }
 
 void StardictdMain::SimpleLookup(const char* sWord)
@@ -701,7 +755,7 @@ bool StardictdMain::SimpleLookupToFloat(const char* sWord, bool bShowIfNotFound)
 			return true;
 		}
 		// delete last word
-		if (bIsPureEnglish(SearchWord)) {
+		if (IsASCII(SearchWord)) {
 			while (EndPointer>=SearchWord && *EndPointer!=' ')
 				EndPointer--;
 			if (EndPointer>=SearchWord)
@@ -724,6 +778,58 @@ bool StardictdMain::SimpleLookupToFloat(const char* sWord, bool bShowIfNotFound)
 	return false;
 }
 
+bool StardictdMain::FuzzyLookupToFloat(const char* sWord, bool bShowIfNotFound)
+{
+	gchar *fuzzy_reslist[MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM];
+	bool Found = oLibs.LookupWithFuzzy(sWord, fuzzy_reslist, MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM, scan_dictmask);
+	if (Found) {
+		int i, count=0;
+		for (i=0; i<MAX_FLOAT_WINDOW_FUZZY_MATCH_ITEM; i++) {
+			if (fuzzy_reslist[i])
+				count++;
+			else
+				break;
+		}
+		gchar ****ppppWord = (gchar ****)g_malloc(sizeof(gchar ***) * count);
+		gchar *****pppppWordData = (gchar *****)g_malloc(sizeof(gchar ****) * count);
+		const gchar **ppOriginWord = (const gchar **)g_malloc(sizeof(gchar *) * count);
+		CurrentIndex *iIndex = (CurrentIndex *)g_malloc(sizeof(CurrentIndex) * scan_dictmask.size());
+
+		gchar ***pppWord;
+		gchar ****ppppWordData;
+		for (i=0;i<count;i++) {
+			bool bFound = false;
+			pppWord = (gchar ***)g_malloc(sizeof(gchar **) * scan_dictmask.size());
+			ppppWordData = (gchar ****)g_malloc(sizeof(gchar ***) * scan_dictmask.size());
+
+			ppOriginWord[i] = fuzzy_reslist[i];
+			for (size_t iLib=0; iLib<scan_dictmask.size(); iLib++)
+				BuildResultData(scan_dictmask, fuzzy_reslist[i], iIndex, NULL, iLib, pppWord, ppppWordData, bFound, 2);
+			//for (size_t iLib=0; iLib<scan_dictmask.size(); iLib++)
+				//BuildVirtualDictData(scan_dictmask, fuzzy_reslist[i], iLib, pppWord, ppppWordData, bFound);
+			if (bFound) {// it is certainly be true.
+				ppppWord[i]=pppWord;
+				pppppWordData[i]=ppppWordData;
+			} else {
+				FreeResultData(scan_dictmask.size(), pppWord, ppppWordData);
+				ppppWord[i]=NULL;
+			}
+		}
+		oFloatWin.AppendTextFuzzy(ppppWord, pppppWordData, ppOriginWord, count, sWord);
+		for (i=0; i<count; i++) {
+			if (ppppWord[i])
+				FreeResultData(scan_dictmask.size(), ppppWord[i], pppppWordData[i]);
+		}
+		g_free(ppppWord);
+		g_free(pppppWordData);
+		g_free(ppOriginWord);
+		g_free(iIndex);
+
+		for (i=0;i<count;i++)
+			g_free(fuzzy_reslist[i]);
+	}
+}
+
 static gchar* GetPureEnglishAlpha(gchar *str)
 {
 	while (*str && (!((*str >= 'a' && *str <='z')||(*str >= 'A' && *str <='Z'))))
@@ -742,7 +848,7 @@ void StardictdMain::query(const gchar* sWord)
 
 void StardictdMain::selectquery(const gchar* sWord)
 {
-	if (bIsPureEnglish(sWord)) {
+	if (IsASCII(sWord)) {
 		if (SimpleLookupToFloat(sWord,false) )  //found
 			return;
 		char *sToken = g_strdup(sWord);
@@ -762,6 +868,11 @@ void StardictdMain::selectquery(const gchar* sWord)
 	}
 }
 
+void StardictdMain::fuzzyquery(const gchar* sWord)
+{
+	FuzzyLookupToFloat(sWord,true);
+}
+
 bool StardictdMain::smartquery(const gchar* sWord, int BeginPos)
 {
 	if (sWord==NULL || sWord[0]=='\0') {
@@ -801,7 +912,7 @@ bool StardictdMain::smartquery(const gchar* sWord, int BeginPos)
 		SearchTimes--;
 		if (!SearchTimes)
 			break;
-		if (bIsPureEnglish(P1)) {
+		if (IsASCII(P1)) {
 			char *P2 = SearchWord + BeginPos;
 			if (g_ascii_isupper(*P2)) {
 				char *P3 = SearchWord + BeginPos;
