/ scripts / fix-curly-quotes.sh
fix-curly-quotes.sh
 1  #!/usr/bin/env bash
 2  # Pre-commit helper: normalize curly quotes and other characters that break parsers.
 3  # Replaces: smart quotes → ASCII, nbsp → space, en/em dash → -, ellipsis → ...; removes ZWSP, ZWNJ, ZWJ, BOM.
 4  # Portable: BSD sed (macOS) and GNU sed; uses gsed on macOS when available.
 5  
 6  set -e
 7  
 8  if [[ "$(uname -s)" = Darwin ]] && command -v gsed &>/dev/null; then
 9    SED="gsed"
10  else
11    SED="sed"
12  fi
13  
14  DQ_LEFT=$(printf '\342\200\234')
15  DQ_RIGHT=$(printf '\342\200\235')
16  SQ_LEFT=$(printf '\342\200\230')
17  SQ_RIGHT=$(printf '\342\200\231')
18  NBSP=$(printf '\302\240')
19  EN_DASH=$(printf '\342\200\223')
20  EM_DASH=$(printf '\342\200\224')
21  ELLIPSIS=$(printf '\342\200\246')
22  ZWSP=$(printf '\342\200\213')
23  ZWNJ=$(printf '\342\200\214')
24  ZWJ=$(printf '\342\200\215')
25  BOM=$(printf '\357\273\277')
26  
27  changed=0
28  
29  for f in "$@"; do
30    [[ -f "$f" ]] || continue
31    tmp=$(mktemp)
32    if "$SED" \
33      -e "s/${DQ_LEFT}/\"/g" \
34      -e "s/${DQ_RIGHT}/\"/g" \
35      -e "s/${SQ_LEFT}/'/g" \
36      -e "s/${SQ_RIGHT}/'/g" \
37      -e "s/${NBSP}/ /g" \
38      -e "s/${EN_DASH}/-/g" \
39      -e "s/${EM_DASH}/-/g" \
40      -e "s/${ELLIPSIS}/.../g" \
41      -e "s/${ZWSP}//g" \
42      -e "s/${ZWNJ}//g" \
43      -e "s/${ZWJ}//g" \
44      -e "s/${BOM}//g" \
45      "$f" >"$tmp"; then
46      if ! cmp -s "$f" "$tmp"; then
47        mv "$tmp" "$f"
48        changed=1
49      else
50        rm -f "$tmp"
51      fi
52    else
53      rm -f "$tmp"
54      exit 1
55    fi
56  done
57  
58  [[ $changed -eq 0 ]]