No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

localcheck 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #!/usr/bin/env bash
  2. warn() { printf "\e[31mwarning:\e[m %s\n" "$*"; ((++n_warnings)); } >&2
  3. note() { printf "\e[33mnotice:\e[m %s\n" "$*"; ((++n_notices)); }
  4. problem() { (( ++sy_total )); printf "\n\e[33mproblem:\e[m %s\n" "$*"; }
  5. is_locale_supported() {
  6. locale -a 2>/dev/null | grep -qsxF "$(get_locale "$1")"
  7. }
  8. get_locale() {
  9. local locale=$1
  10. if [[ $locale == *.* ]]; then
  11. locale=${locale%%.*}.$(get_charset "$locale")
  12. fi
  13. echo "$locale"
  14. }
  15. get_charset() {
  16. local locale=$1 charset=''
  17. if [[ $locale == *.* ]]; then
  18. charset=${locale#*.}
  19. charset=${charset//-/}
  20. charset=${charset,,}
  21. else
  22. charset='default'
  23. fi
  24. echo "$charset"
  25. }
  26. check_setting() {
  27. local name=$1 value=$2
  28. [[ -z $value ]] && return
  29. if ! is_locale_supported "$value"; then
  30. warn "$name: unsupported locale \"$value\""
  31. (( ++sy_missing ))
  32. local xlocale=$(get_locale "$value")
  33. sy_missing_locs["$xlocale"]=y
  34. fi
  35. local charset=$(get_charset "$value")
  36. if [[ $charset != "utf8" ]]; then
  37. warn "$name: non-UTF8 locale \"$value\""
  38. sy_nonutf8_vars["$name"]=y
  39. (( ++sy_nonutf8 ))
  40. fi
  41. if [[ "$charset" != "$main_charset" ]]; then
  42. if [[ "${name##* }" == "LANG" ]]; then
  43. return
  44. fi
  45. warn "$name: charset does not match LANG ($charset | $main_charset)"
  46. (( ++sy_charmismatch ))
  47. fi
  48. }
  49. shopt -s extglob
  50. checking_term=0
  51. checking_parent=0
  52. guessing_parent=""
  53. declare -i pid_shell=$PPID
  54. declare -i pid_term=$(ps -o 'ppid=' $pid_shell)
  55. declare -i pid_parent=$(ps -o 'ppid=' $pid_term)
  56. if (( pid_term > 1 )) && [[ -r "/proc/$pid_term/environ" ]]; then
  57. checking_term=1
  58. if (( pid_parent > 1 )); then
  59. if [[ -r "/proc/$pid_parent/environ" ]]; then
  60. checking_parent=1
  61. fi
  62. elif [[ $SESSION_MANAGER == */tmp/.ICE-unix/+([0-9]) ]]; then
  63. guessing_parent="from \$SESSION_MANAGER"
  64. pid_parent=${SESSION_MANAGER##*/tmp/.ICE-unix/}
  65. if [[ -r "/proc/$pid_parent/environ" ]]; then
  66. checking_parent=1
  67. fi
  68. fi
  69. fi
  70. ps_shell=$(ps -o 'pid=,command=' $pid_shell)
  71. ps_terminal=$(ps -o 'pid=,command=' $pid_term)
  72. ps_parent=$(ps -o 'pid=,command=' $pid_parent)
  73. echo " * Parent: $ps_parent"
  74. echo " * Terminal: $ps_terminal"
  75. echo " * Shell: $ps_shell"
  76. echo ""
  77. unset ${!sy_*}
  78. declare -A sy_missing_locs
  79. declare -A sy_nonutf8_vars
  80. # Read terminal's environment
  81. vars=("LC_CTYPE" "LC_NUMERIC" "LC_TIME" "LC_COLLATE" "LC_MONETARY" "LC_MESSAGES"
  82. "LC_PAPER" "LC_NAME" "LC_ADDRESS" "LC_TELEPHONE" "LC_MEASUREMENT"
  83. "LC_IDENTIFICATION" "LC_ALL")
  84. if (( checking_term )); then
  85. unset ${!TERM_*}
  86. while IFS='=' read -d '' name value; do
  87. if [[ $name == LANG || $name == LC_* ]]; then
  88. declare "TERM_$name"="$value"
  89. fi
  90. done < "/proc/$pid_term/environ"
  91. elif [[ $ps_terminal == *' sshd:'* ]]; then
  92. warn "I cannot check your terminal's settings over SSH, skipping."
  93. else
  94. warn "Terminal's environment is inaccessible, skipping shell=term checks."
  95. fi
  96. if (( checking_parent )); then
  97. if [[ $guessing_parent ]]; then
  98. note "Tried to guess parent process $guessing_parent."
  99. fi
  100. unset ${!PARN_*}
  101. while IFS='=' read -d '' name value; do
  102. if [[ $name == LANG || $name == LC_* ]]; then
  103. declare "PARN_$name"="$value"
  104. fi
  105. done < "/proc/$pid_parent/environ"
  106. elif (( checking_term )); then
  107. warn "Parent's environment is inaccessible, skipping term=parent checks."
  108. else
  109. note "Also skipping term=parent checks."
  110. fi
  111. # Basic checks
  112. if [[ -z $LANG ]]; then
  113. warn "(shell) LANG: not set"
  114. main_charset='default'
  115. (( ++sy_nolang ))
  116. else
  117. main_charset=$(get_charset "$LANG")
  118. fi
  119. if (( checking_term )); then
  120. if [[ -z $TERM_LANG ]]; then
  121. warn "(term) LANG: not set"
  122. t_main_charset='default'
  123. (( ++sy_nolang ))
  124. else
  125. t_main_charset=$(get_charset "$TERM_LANG")
  126. fi
  127. fi
  128. if (( checking_parent )); then
  129. if [[ -z $PARN_LANG ]]; then
  130. warn "(parent) LANG: not set"
  131. p_main_charset='default'
  132. (( ++sy_nolang ))
  133. else
  134. p_main_charset=$(get_charset "$PARN_LANG")
  135. fi
  136. fi
  137. for name in LANG "${vars[@]}"; do
  138. if [[ "$name" == "LC_COLLATE" ]]; then
  139. # skip LC_COLLATE on purpose, having it as 'C' is fine
  140. continue
  141. fi
  142. value=${!name}
  143. locale=$(get_locale "$value")
  144. charset=$(get_charset "$value")
  145. (( checking_term )) && {
  146. t_name="TERM_$name"
  147. t_value=${!t_name}
  148. t_locale=$(get_locale "$t_value")
  149. t_charset=$(get_charset "$t_value")
  150. }
  151. (( checking_parent )) && {
  152. p_name="PARN_$name"
  153. p_value=${!p_name}
  154. p_locale=$(get_locale "$p_value")
  155. p_charset=$(get_charset "$p_value")
  156. }
  157. check_setting "(shell) $name" "$value"
  158. (( checking_term )) &&
  159. check_setting "(term) $name" "$t_value"
  160. (( checking_parent )) &&
  161. check_setting "(parent) $name" "$p_value"
  162. if [[ "$name" == "LC_ALL" && -n "$value" ]]; then
  163. warn "$name: should not be set ($value)"
  164. (( ++sy_lcall ))
  165. fi
  166. (( checking_term )) && {
  167. if [[ -n "$value" && -z "$t_value" ]]; then
  168. warn "$name: set by shell but not terminal ($value | none)"
  169. (( ++sy_mismatch ))
  170. elif [[ -z "$value" && -n "$t_value" ]]; then
  171. warn "$name: set by terminal but not shell (none | $t_value)"
  172. (( ++sy_mismatch ))
  173. elif [[ "$charset" != "$t_charset" ]]; then
  174. warn "$name: charset mismatch between shell and terminal ($locale | $t_locale)"
  175. (( ++sy_mismatch ))
  176. elif [[ "$locale" != "$t_locale" ]]; then
  177. warn "$name: lang mismatch between shell and terminal ($locale | $t_locale)"
  178. (( ++sy_mismatch ))
  179. fi
  180. }
  181. (( checking_parent )) && {
  182. if [[ -n "$t_value" && -z "$p_value" ]]; then
  183. warn "$name: set by terminal but not parent ($t_value | none)"
  184. (( ++sy_p_mismatch ))
  185. elif [[ -z "$t_value" && -n "$p_value" ]]; then
  186. warn "$name: set by parent but not terminal (none | $p_value)"
  187. (( ++sy_p_mismatch ))
  188. elif [[ "$t_charset" != "$p_charset" ]]; then
  189. warn "$name: charset mismatch between terminal and parent ($t_locale | $p_locale)"
  190. (( ++sy_p_mismatch ))
  191. elif [[ "$t_locale" != "$p_locale" ]]; then
  192. warn "$name: lang mismatch between terminal and parent ($t_locale | $p_locale)"
  193. (( ++sy_p_mismatch ))
  194. fi
  195. }
  196. done
  197. if [[ ${LANG,,} == *'.utf8' ]]; then
  198. (( ++sy_utf8_dash ))
  199. fi
  200. # Display final results
  201. if (( sy_nolang )) && [[ $LANG && -z $TERM_LANG ]]; then
  202. problem "Your terminal is missing \$LANG in its environment."
  203. echo " * Locale variables must be set for the terminal emulator itself"
  204. echo " (and for the entire session), not only for the shell."
  205. elif (( sy_nolang )) && [[ -z $LANG && $TERM_LANG ]]; then
  206. problem "Your shell is missing \$LANG in its environment."
  207. echo " * Even though your terminal has the correct \$LANG ($TERM_LANG),"
  208. echo " it was removed by your shell's .profile, .bashrc or similar files."
  209. elif (( sy_nolang )); then
  210. problem "You do not have \$LANG set."
  211. echo " * It must be set to a <lang>.utf-8 locale."
  212. fi
  213. if (( sy_mismatch )); then
  214. problem "Shell and terminal have different locale settings."
  215. echo " * Your .bashrc or similar startup scripts may be overriding them."
  216. fi
  217. if (( sy_p_mismatch )); then
  218. : ${parn_locale:=$PARN_LC_ALL}; : ${parn_locale:=$PARN_LC_CTYPE}
  219. : ${parn_locale:=$PARN_LANG}; : ${parn_locale:=empty}
  220. : ${term_locale:=$TERM_LC_ALL}; : ${term_locale:=$TERM_LC_CTYPE}
  221. : ${term_locale:=$TERM_LANG}; : ${term_locale:=empty}
  222. problem "Terminal and its parent have different locale settings."
  223. echo " * Your session doesn't have the right locale set, and your window manager"
  224. echo " is launching all programs using the $parn_locale locale. But your terminal"
  225. echo " hides the problem by setting its own locale to $term_locale."
  226. echo " * Fix your system to set the locale at login or session startup time."
  227. fi
  228. if (( sy_lcall )); then
  229. problem "You have \$LC_ALL set; it overrides all other settings."
  230. echo " * Do not set \$LC_ALL unless absolutely required."
  231. echo " For normal usage, setting \$LANG should be enough."
  232. fi
  233. if (( sy_nonutf8 )); then
  234. problem "Your current locale is using a legacy charset."
  235. echo " * The incorrect variables are:"
  236. for var in "${!sy_nonutf8_vars[@]}"; do
  237. varname=${var#* }
  238. printf ' - %-20s (currently "%s")\n' "$var" "${!varname}"
  239. done
  240. echo " * Change your locales to their UTF-8 variants."
  241. fi
  242. if (( sy_charmismatch )) && [[ $LANG ]]; then
  243. problem "Your locale settings use different charsets."
  244. echo " * If any \$LC_* variables are set, they should use the same charset as \$LANG."
  245. fi
  246. if (( sy_missing )); then
  247. problem "Your current locale is missing from the system."
  248. echo " * The missing locales are:"
  249. printf ' - \e[1m%s\e[m\n' "${!sy_missing_locs[@]}"
  250. echo " * Make sure /etc/locale.gen has the apropriate lines uncommented."
  251. echo " After editing the file, run 'locale-gen' as root."
  252. fi
  253. if (( sy_utf8_dash )); then
  254. problem "\$LANG is missing a dash in the charset."
  255. echo " * Even though 'utf-8' and 'utf8' are equivalent, some poorly-written programs"
  256. echo " (such as 'tree') consider them different and will not work with the latter."
  257. echo " * To fix this, change \$LANG from \"$LANG\" to \"${LANG%.*}.utf-8\""
  258. fi
  259. if (( n_warnings + n_notices )); then
  260. echo ""
  261. fi
  262. if (( sy_total > 0 )); then
  263. echo -e "\e[1m$sy_total problems found.\e[m Here's a quick UTF-8 test for you: --> \xe2\x98\x85 <--"
  264. elif (( !checking_term )); then
  265. echo -e "Looks good, but you still need to check your terminal: --> \xe2\x98\x85 <--"
  266. else
  267. printf "Looks good. \xe2\x99\xa5\n"
  268. echo " * You are using the $LANG locale."
  269. echo " * Shell's locale matches terminal's locale."
  270. if (( checking_parent )); then
  271. echo " * Terminal's locale matches parent process locale."
  272. else
  273. echo " * Could not check if terminal and parent's locale settings match."
  274. fi
  275. fi
  276. if (( sy_total > 0 || !checking_term )); then
  277. echo " * a star -- font and terminal are okay."
  278. echo " * 3 question marks -- your terminal does not correctly interpret UTF-8".
  279. echo " * a box or rectangle -- UTF-8 works fine, but you need a better font."
  280. echo " * empty area -- you ${B}really${R} need a better font or something."
  281. fi