PageRenderTime 2489ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/adblock/install.sh

https://gitlab.com/rrevanth/dotfiles
Shell | 613 lines | 517 code | 52 blank | 44 comment | 82 complexity | 4cf811cb31b5a54ff457cfe45fbcf05e MD5 | raw file
  1. #!/bin/sh
  2. # Version: 2.0.1
  3. # Author: Héctor Molinero Fernández <hector@molinero.xyz>
  4. # Repository: https://github.com/hectorm/hblock
  5. # License: MIT, https://opensource.org/licenses/MIT
  6. set -eu
  7. export LC_ALL=C
  8. # shellcheck disable=SC2039
  9. HOSTNAME=${HOSTNAME-$(uname -n)}
  10. # Builtin values
  11. BUILTIN_HEADER=$(cat <<-EOF
  12. 127.0.0.1 localhost $HOSTNAME
  13. 255.255.255.255 broadcasthost
  14. ::1 localhost ip6-localhost ip6-loopback
  15. fe00::0 ip6-localnet
  16. ff00::0 ip6-mcastprefix
  17. ff02::1 ip6-allnodes
  18. ff02::2 ip6-allrouters
  19. ff02::3 ip6-allhosts
  20. EOF
  21. )
  22. BUILTIN_FOOTER=''
  23. BUILTIN_SOURCES=$(cat <<-'EOF'
  24. https://raw.githubusercontent.com/hectorm/hmirror/master/data/adaway.org/list.txt
  25. https://raw.githubusercontent.com/hectorm/hmirror/master/data/adblock-nocoin-list/list.txt
  26. https://raw.githubusercontent.com/hectorm/hmirror/master/data/adguard-simplified/list.txt
  27. https://raw.githubusercontent.com/hectorm/hmirror/master/data/disconnect.me-ad/list.txt
  28. https://raw.githubusercontent.com/hectorm/hmirror/master/data/disconnect.me-malvertising/list.txt
  29. https://raw.githubusercontent.com/hectorm/hmirror/master/data/disconnect.me-malware/list.txt
  30. https://raw.githubusercontent.com/hectorm/hmirror/master/data/disconnect.me-tracking/list.txt
  31. https://raw.githubusercontent.com/hectorm/hmirror/master/data/easylist/list.txt
  32. https://raw.githubusercontent.com/hectorm/hmirror/master/data/easyprivacy/list.txt
  33. https://raw.githubusercontent.com/hectorm/hmirror/master/data/eth-phishing-detect/list.txt
  34. https://raw.githubusercontent.com/hectorm/hmirror/master/data/fademind-add.2o7net/list.txt
  35. https://raw.githubusercontent.com/hectorm/hmirror/master/data/fademind-add.dead/list.txt
  36. https://raw.githubusercontent.com/hectorm/hmirror/master/data/fademind-add.risk/list.txt
  37. https://raw.githubusercontent.com/hectorm/hmirror/master/data/fademind-add.spam/list.txt
  38. https://raw.githubusercontent.com/hectorm/hmirror/master/data/kadhosts/list.txt
  39. https://raw.githubusercontent.com/hectorm/hmirror/master/data/malwaredomainlist.com/list.txt
  40. https://raw.githubusercontent.com/hectorm/hmirror/master/data/malwaredomains.com-immortaldomains/list.txt
  41. https://raw.githubusercontent.com/hectorm/hmirror/master/data/malwaredomains.com-justdomains/list.txt
  42. https://raw.githubusercontent.com/hectorm/hmirror/master/data/matomo.org-spammers/list.txt
  43. https://raw.githubusercontent.com/hectorm/hmirror/master/data/mitchellkrogza-badd-boyz-hosts/list.txt
  44. https://raw.githubusercontent.com/hectorm/hmirror/master/data/pgl.yoyo.org/list.txt
  45. https://raw.githubusercontent.com/hectorm/hmirror/master/data/ransomwaretracker.abuse.ch/list.txt
  46. https://raw.githubusercontent.com/hectorm/hmirror/master/data/someonewhocares.org/list.txt
  47. https://raw.githubusercontent.com/hectorm/hmirror/master/data/spam404.com/list.txt
  48. https://raw.githubusercontent.com/hectorm/hmirror/master/data/stevenblack/list.txt
  49. https://raw.githubusercontent.com/hectorm/hmirror/master/data/winhelp2002.mvps.org/list.txt
  50. https://raw.githubusercontent.com/hectorm/hmirror/master/data/zerodot1-coinblockerlists-browser/list.txt
  51. https://raw.githubusercontent.com/hectorm/hmirror/master/data/zeustracker.abuse.ch/list.txt
  52. https://dbl.oisd.nl/
  53. EOF
  54. )
  55. BUILTIN_WHITELIST=$(cat <<-'EOF'
  56. EOF
  57. )
  58. BUILTIN_BLACKLIST=$(cat <<-'EOF'
  59. pubads.g.doubleclick.net
  60. securepubads.g.doubleclick.net
  61. addthis.com
  62. addtoany.com
  63. amung.us
  64. bebi.com
  65. bodelen.com
  66. clcknads.pro
  67. coostuni.com
  68. defpush.com
  69. fuxoasim.link
  70. glotchat.click
  71. jeeshapi.net
  72. juicyads.com
  73. lovacmar.click
  74. mgid.com
  75. padstm.com
  76. revrtb.com
  77. riluaneth.com
  78. rudsools.net
  79. vidcpm.com
  80. vukhhjzd.com
  81. wallstrads.com
  82. wynather.com
  83. yadro.ru
  84. yandex.ru
  85. ytimg.com
  86. EOF
  87. )
  88. # Check if a program exists
  89. exists() {
  90. # shellcheck disable=SC2230
  91. if command -v true; then command -v -- "$1"
  92. elif eval type type; then eval type -- "$1"
  93. else which -- "$1"; fi >/dev/null 2>&1
  94. }
  95. # Escape strings in sed
  96. # See: https://stackoverflow.com/a/29613573
  97. quoteRe() { printf -- '%s' "$1" | sed -e 's/[^^]/[&]/g; s/\^/\\^/g; $!a'\\''"$(printf '\n')"'\\n' | tr -d '\n'; }
  98. quoteSubst() { printf -- '%s' "$1" | sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g'; }
  99. # Remove comments from string
  100. removeComments() { printf -- '%s' "$1" | sed -e 's/[[:blank:]]*#.*//'; }
  101. # Translate true/false to yes/no
  102. getBoolVal() { [ "$1" = true ] && s='yes' || s='no'; printf -- '%s' "$s"; }
  103. # Print to stdout if quiet mode is not enabled
  104. printStdout() {
  105. if [ "$quiet" != true ]; then
  106. # shellcheck disable=SC2059
  107. printf -- "$@"
  108. fi
  109. }
  110. # Print to stderr
  111. printStderr() {
  112. # shellcheck disable=SC2059
  113. >&2 printf -- "$@"
  114. }
  115. # Print informational message
  116. logInfo() {
  117. printStdout ' - %s\n' "$@"
  118. }
  119. # Print action message
  120. logAction() {
  121. if [ "$color" = true ]; then
  122. printStdout '\033[1;33m + \033[1;32m%s \033[0m\n' "$@"
  123. else
  124. printStdout ' + %s \n' "$@"
  125. fi
  126. }
  127. # Print error message
  128. logError() {
  129. if [ "$color" = true ]; then
  130. printStderr '\033[1;33m + \033[1;31m%s \033[0m\n' "$@"
  131. else
  132. printStderr ' + %s \n' "$@"
  133. fi
  134. }
  135. # Create temporary file
  136. createTempFile() {
  137. if exists mktemp; then mktemp
  138. else # Since POSIX does not specify mktemp utility, use this as fallback
  139. tempCounter=${tempCounter:-9999}
  140. tempFile="${TMPDIR:-/tmp}/hblock.$$.$((tempCounter+=1))"
  141. rm -f -- "$tempFile" && (umask 077 && touch -- "$tempFile")
  142. printf -- '%s\n' "$tempFile"
  143. fi
  144. }
  145. # Print to stdout the contents of a URL
  146. fetchUrl() {
  147. # If the protocol is "file://" we can omit the download and simply use cat
  148. if [ "${1#file://}" != "$1" ]; then cat -- "${1#file://}"
  149. else
  150. userAgent='Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0'
  151. if exists curl; then curl -fsSL -A "$userAgent" -- "$1";
  152. elif exists wget; then wget -qO- -U "$userAgent" -- "$1";
  153. else
  154. logError 'Either wget or curl are required for this script'
  155. exit 1
  156. fi
  157. fi
  158. }
  159. # Show help and quit
  160. showHelp() {
  161. if [ $# -eq 0 ]; then
  162. printStdout '%s\n' "$(cat <<-'EOF'
  163. Usage: hblock [options...]
  164. -O, --output <FILE>
  165. Output file location
  166. (default: "/etc/hosts" file)
  167. -H, --header <FILE>
  168. Content to be included at the beginning of the output file
  169. (default: HBLOCK_HEADER environment variable,
  170. "/etc/hblock.d/header" file or builtin value)
  171. -F, --footer <FILE>
  172. Content to be included at the end of the output file
  173. (default: HBLOCK_FOOTER environment variable,
  174. "/etc/hblock.d/footer" file or builtin value)
  175. -S, --sources <FILE>
  176. Newline separated URLs used to generate the blocklist
  177. (default: HBLOCK_SOURCES environment variable,
  178. "/etc/hblock.d/sources.list" file or builtin value)
  179. -W, --whitelist <FILE>
  180. Newline separated domains to be removed from the blocklist
  181. (default: HBLOCK_WHITELIST environment variable,
  182. "/etc/hblock.d/whitelist.list" file or builtin value)
  183. -B, --blacklist <FILE>
  184. Newline separated domains to be added to the blocklist
  185. (default: HBLOCK_BLACKLIST environment variable,
  186. "/etc/hblock.d/blacklist.list" file or builtin value)
  187. -R, --redirection <REDIRECTION>
  188. Redirection for all entries in the blocklist
  189. (default: 0.0.0.0)
  190. -T, --template <TEMPLATE>
  191. POSIX BREs replacement applied to each entry
  192. \1 = <DOMAIN>, \2 = <REDIRECTION>
  193. (default: \2 \1)
  194. -C, --comment <COMMENT>
  195. Character used for comments
  196. (default: #)
  197. -b, --backup [DIRECTORY]
  198. Make a time-stamped backup in <DIRECTORY>
  199. (default: output file directory)
  200. -l, --lenient
  201. Match all entries from sources, regardless of their IP
  202. (default: 0.0.0.0, 127.0.0.1 or none)
  203. -r, --enable-regex-whitelist
  204. Use POSIX BREs instead of fixed strings
  205. -i, --ignore-download-error
  206. Do not abort if a download error occurs
  207. -c, --color <auto|true|false>
  208. Colorize the output
  209. (default: auto)
  210. -q, --quiet
  211. Suppress non-error messages
  212. -v, --version
  213. Show version number and quit
  214. -h, --help
  215. Show this help and quit
  216. Report bugs to: <https://github.com/hectorm/hblock/issues>.
  217. EOF
  218. )"
  219. exit 0
  220. else
  221. [ -n "$1" ] && printStderr '%s\n' "Illegal option $1"
  222. printStderr '%s\n' "Try 'hblock --help' for more information"
  223. exit 1
  224. fi
  225. }
  226. # Show version number and quit
  227. showVersion() {
  228. printStdout '%s\n' '2.0.1'
  229. exit 0
  230. }
  231. main() {
  232. outputFile='/etc/hosts'
  233. headerFile='/etc/hblock.d/header'; defaultHeaderFile=$headerFile; header=''
  234. footerFile='/etc/hblock.d/footer'; defaultFooterFile=$footerFile; footer=''
  235. sourcesFile='/etc/hblock.d/sources.list'; defaultSourcesFile=$sourcesFile; sources=''
  236. whitelistFile='/etc/hblock.d/whitelist.list'; defaultWhitelistFile=$whitelistFile; whitelist=''
  237. blacklistFile='/etc/hblock.d/blacklist.list'; defaultBlacklistFile=$blacklistFile; blacklist=''
  238. redirection='0.0.0.0'
  239. template='\2 \1'
  240. comment='#'
  241. backup=false
  242. lenient=false
  243. enableWhitelistRegex=false
  244. ignoreDownloadError=false
  245. color=auto
  246. quiet=false
  247. # Transform long options to short ones
  248. for opt in "$@"; do
  249. shift
  250. case "$opt" in
  251. '--output') set -- "$@" '-O' ;;
  252. '--header') set -- "$@" '-H' ;;
  253. '--footer') set -- "$@" '-F' ;;
  254. '--sources') set -- "$@" '-S' ;;
  255. '--whitelist') set -- "$@" '-W' ;;
  256. '--blacklist') set -- "$@" '-B' ;;
  257. '--redirection') set -- "$@" '-R' ;;
  258. '--template') set -- "$@" '-T' ;;
  259. '--comment') set -- "$@" '-C' ;;
  260. '--backup') set -- "$@" '-b' ;;
  261. '--lenient') set -- "$@" '-l' ;;
  262. '--enable-regex-whitelist') set -- "$@" '-r' ;;
  263. '--ignore-download-error') set -- "$@" '-i' ;;
  264. '--color') set -- "$@" '-c' ;;
  265. '--quiet') set -- "$@" '-q' ;;
  266. '--version') set -- "$@" '-v' ;;
  267. '--help') set -- "$@" '-h' ;;
  268. *) set -- "$@" "$opt"
  269. esac
  270. done
  271. # Set omitted arguments to empty strings
  272. for opt in "$@"; do
  273. shift
  274. case "$opt" in
  275. -*b)
  276. if a="$*"; [ -z "$a" ] || [ "${a#\-}x" != "${a}x" ]
  277. then set -- "$@" "$opt" ''
  278. else set -- "$@" "$opt"
  279. fi
  280. ;;
  281. *) set -- "$@" "$opt"
  282. esac
  283. done
  284. # Read short options
  285. OPTIND=1
  286. while getopts ':O:H:F:S:W:B:R:T:C:b:lric:qvh-:' opt; do
  287. case "$opt" in
  288. 'O') outputFile="$OPTARG" ;;
  289. 'H') headerFile="$OPTARG" ;;
  290. 'F') footerFile="$OPTARG" ;;
  291. 'S') sourcesFile="$OPTARG" ;;
  292. 'W') whitelistFile="$OPTARG" ;;
  293. 'B') blacklistFile="$OPTARG" ;;
  294. 'R') redirection="$OPTARG" ;;
  295. 'T') template="$OPTARG" ;;
  296. 'C') comment="$OPTARG" ;;
  297. 'b') backup=true; backupDir="$OPTARG" ;;
  298. 'l') lenient=true ;;
  299. 'r') enableWhitelistRegex=true ;;
  300. 'i') ignoreDownloadError=true ;;
  301. 'c') color="$OPTARG" ;;
  302. 'q') quiet=true ;;
  303. 'v') showVersion ;;
  304. 'h') showHelp ;;
  305. '-') showHelp "--$OPTARG" ;;
  306. *) showHelp "-$OPTARG"
  307. esac
  308. done
  309. # Check color support
  310. if [ "$color" = auto ]; then
  311. [ -t 1 ] && color=true || color=false
  312. fi
  313. # Priority: $headerFile > $HBLOCK_HEADER > $defaultHeaderFile > builtin
  314. if [ "$headerFile" != "$defaultHeaderFile" ]; then
  315. header=$(cat -- "$headerFile")
  316. headerOrigin=$headerFile
  317. elif [ "${HBLOCK_HEADER+def}" = def ]; then
  318. header=$HBLOCK_HEADER
  319. headerOrigin=environment
  320. elif [ -f "$headerFile" ]; then
  321. header=$(cat -- "$headerFile")
  322. headerOrigin=$headerFile
  323. else
  324. header=$BUILTIN_HEADER
  325. headerOrigin=builtin
  326. fi
  327. # Priority: $footerFile > $HBLOCK_FOOTER > $defaultFooterFile > builtin
  328. if [ "$footerFile" != "$defaultFooterFile" ]; then
  329. footer=$(cat -- "$footerFile")
  330. footerOrigin=$footerFile
  331. elif [ "${HBLOCK_FOOTER+def}" = def ]; then
  332. footer=$HBLOCK_FOOTER
  333. footerOrigin=environment
  334. elif [ -f "$footerFile" ]; then
  335. footer=$(cat -- "$footerFile")
  336. footerOrigin=$footerFile
  337. else
  338. footer=$BUILTIN_FOOTER
  339. footerOrigin=builtin
  340. fi
  341. # Priority: $sourcesFile > $HBLOCK_SOURCES > $defaultSourcesFile > builtin
  342. if [ "$sourcesFile" != "$defaultSourcesFile" ]; then
  343. sources=$(cat -- "$sourcesFile")
  344. sourcesOrigin=$sourcesFile
  345. elif [ "${HBLOCK_SOURCES+def}" = def ]; then
  346. sources=$HBLOCK_SOURCES
  347. sourcesOrigin=environment
  348. elif [ -f "$sourcesFile" ]; then
  349. sources=$(cat -- "$sourcesFile")
  350. sourcesOrigin=$sourcesFile
  351. else
  352. sources=$BUILTIN_SOURCES
  353. sourcesOrigin=builtin
  354. fi
  355. sources=$(removeComments "$sources")
  356. # Priority: $whitelistFile > $HBLOCK_WHITELIST > $defaultWhitelistFile > builtin
  357. if [ "$whitelistFile" != "$defaultWhitelistFile" ]; then
  358. whitelist=$(cat -- "$whitelistFile")
  359. whitelistOrigin=$whitelistFile
  360. elif [ "${HBLOCK_WHITELIST+def}" = def ]; then
  361. whitelist=$HBLOCK_WHITELIST
  362. whitelistOrigin=environment
  363. elif [ -f "$whitelistFile" ]; then
  364. whitelist=$(cat -- "$whitelistFile")
  365. whitelistOrigin=$whitelistFile
  366. else
  367. whitelist=$BUILTIN_WHITELIST
  368. whitelistOrigin=builtin
  369. fi
  370. whitelist=$(removeComments "$whitelist")
  371. # Priority: $blacklistFile > $HBLOCK_BLACKLIST > $defaultBlacklistFile > builtin
  372. if [ "$blacklistFile" != "$defaultBlacklistFile" ]; then
  373. blacklist=$(cat -- "$blacklistFile")
  374. blacklistOrigin=$blacklistFile
  375. elif [ "${HBLOCK_BLACKLIST+def}" = def ]; then
  376. blacklist=$HBLOCK_BLACKLIST
  377. blacklistOrigin=environment
  378. elif [ -f "$blacklistFile" ]; then
  379. blacklist=$(cat -- "$blacklistFile")
  380. blacklistOrigin=$blacklistFile
  381. else
  382. blacklist=$BUILTIN_BLACKLIST
  383. blacklistOrigin=builtin
  384. fi
  385. blacklist=$(removeComments "$blacklist")
  386. logAction 'Configuration:'
  387. logInfo "Output: $outputFile"
  388. logInfo "Header: $headerOrigin"
  389. logInfo "Footer: $footerOrigin"
  390. logInfo "Sources: $sourcesOrigin"
  391. logInfo "Whitelist: $whitelistOrigin"
  392. logInfo "Blacklist: $blacklistOrigin"
  393. logInfo "Redirection: $redirection"
  394. logInfo "Template: $template"
  395. logInfo "Comment: $comment"
  396. logInfo "Backup: $(getBoolVal "$backup")"
  397. logInfo "Lenient: $(getBoolVal "$lenient")"
  398. logInfo "Enable regex in whitelist: $(getBoolVal "$enableWhitelistRegex")"
  399. logInfo "Ignore download error: $(getBoolVal "$ignoreDownloadError")"
  400. # Create temporary blocklist file
  401. blocklistFile=$(createTempFile)
  402. rmtemp() { rm -f -- "$blocklistFile" "$blocklistFile".aux.*; }
  403. trap rmtemp EXIT
  404. logAction 'Downloading lists...'
  405. _IFS=$IFS; IFS="$(printf '\nx')"; IFS="${IFS%x}"
  406. for url in $sources; do
  407. logInfo "$url"
  408. fetchUrl "$url" >> "$blocklistFile" && exitCode=0 || exitCode=$?
  409. if [ "$exitCode" -ne 0 ] && [ "$ignoreDownloadError" != true ]; then
  410. logError 'Download failed'
  411. exit 1
  412. fi
  413. done
  414. IFS=$_IFS
  415. logAction 'Parsing lists...'
  416. if [ -s "$blocklistFile" ]; then
  417. logInfo 'Remove carriage return'
  418. tr -d '\r' \
  419. < "$blocklistFile" > "$blocklistFile.aux.1" \
  420. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  421. logInfo 'Transform to lowercase'
  422. tr '[:upper:]' '[:lower:]' \
  423. < "$blocklistFile" > "$blocklistFile.aux.1" \
  424. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  425. logInfo 'Remove comments'
  426. sed -e 's/#.*//' \
  427. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  428. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  429. logInfo 'Trim spaces'
  430. sed -e 's/^[[:blank:]]*//' \
  431. -e 's/[[:blank:]]*$//' \
  432. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  433. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  434. logInfo 'Match hosts lines'
  435. if [ "$lenient" = true ]; then
  436. # This regex would be ideal, but it is not POSIX-compliant.
  437. # ipOctetRegex='\(25[0-5]\|2[0-4][0-9]\|[01]\{0,1\}[0-9][0-9]\{0,1\}\)'
  438. # ipRegex="\\($ipOctetRegex\\.$ipOctetRegex\\.$ipOctetRegex\\.$ipOctetRegex\\)"
  439. ipRegex='\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}'
  440. else
  441. # Same as above.
  442. # ipRegex='\(\(0\.0\.0\.0\)\|\(127\.0\.0\.1\)\)'
  443. ipRegex='\(0\.0\.0\.0\)\{0,1\}\(127\.0\.0\.1\)\{0,1\}'
  444. fi
  445. domainRegex='\([0-9a-z_-]\{1,63\}\.\)\{1,\}[a-z][0-9a-z_-]\{1,62\}'
  446. sed -n \
  447. -e "/^\\(${ipRegex}[[:blank:]]\\{1,\\}\\)\\{0,1\\}$domainRegex$/p" \
  448. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  449. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  450. logInfo 'Remove reserved TLDs'
  451. sed -e '/\.example$/d' \
  452. -e '/\.invalid$/d' \
  453. -e '/\.local$/d' \
  454. -e '/\.localdomain$/d' \
  455. -e '/\.localhost$/d' \
  456. -e '/\.test$/d' \
  457. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  458. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  459. logInfo 'Remove destination IPs'
  460. sed -e 's/^.\{1,\}[[:blank:]]\{1,\}//' \
  461. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  462. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  463. fi
  464. # This domain is used to check if hBlock is enabled
  465. printf -- '%s\n' 'hblock-check.molinero.xyz' >> "$blocklistFile"
  466. if [ -n "$blacklist" ]; then
  467. logInfo 'Apply blacklist'
  468. printf -- '%s' "$blacklist" >> "$blocklistFile"
  469. fi
  470. logInfo 'Sort entries'
  471. sort -- "$blocklistFile" | uniq | sed -e '/^$/d' > "$blocklistFile.aux.1" \
  472. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  473. if [ -n "$whitelist" ]; then
  474. logInfo 'Apply whitelist'
  475. if [ "$enableWhitelistRegex" = true ]; then
  476. _IFS=$IFS; IFS="$(printf '\nx')"; IFS="${IFS%x}"
  477. for regex in $whitelist; do
  478. sed -e "/$regex/d" \
  479. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  480. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  481. done
  482. IFS=$_IFS
  483. else
  484. printf -- '%s' "$whitelist" | sort | uniq > "$blocklistFile.aux.2"
  485. comm -23 -- "$blocklistFile" "$blocklistFile.aux.2" > "$blocklistFile.aux.1" \
  486. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile" \
  487. && rm -f -- "$blocklistFile.aux.2"
  488. fi
  489. fi
  490. # Count blocked domains
  491. blocklistCount=$(wc -l < "$blocklistFile" | tr -d ' ');
  492. logInfo 'Apply format template'
  493. sed -e "s/$/\\t$(quoteSubst "$redirection")/" \
  494. -e "s/^\\(.*\\)\\t\\(.*\\)$/$template/" \
  495. -- "$blocklistFile" > "$blocklistFile.aux.1" \
  496. && mv -f -- "$blocklistFile.aux.1" "$blocklistFile"
  497. # Define "C" variable for convenience
  498. C=$comment
  499. if [ "$outputFile" != - ] && [ -f "$outputFile" ]; then
  500. # Backup procedure
  501. if [ "$backup" = true ]; then
  502. logAction 'Backing up original file...'
  503. [ -z "$backupDir" ] && backupDir=$(dirname -- "$outputFile")
  504. outputBackup="$backupDir/$(basename -- "$outputFile").$(date +%s).bak"
  505. if touch -- "$outputBackup" >/dev/null 2>&1; then
  506. cp -af -- "$outputFile" "$outputBackup"
  507. elif exists sudo; then
  508. sudo cp -af -- "$outputFile" "$outputBackup"
  509. else
  510. logError "Cannot write '$outputBackup': permission denied"
  511. exit 1
  512. fi
  513. fi
  514. fi
  515. logAction 'Generating output file...'
  516. printOutputFile() {
  517. if [ -n "$C" ]; then
  518. printf -- '%s\n' "$(cat <<-EOF
  519. $C Author: Héctor Molinero Fernández <hector@molinero.xyz>
  520. $C Repository: https://github.com/hectorm/hblock
  521. $C Last updated: $(date -u)
  522. $C Blocked domains: $blocklistCount
  523. EOF
  524. )"
  525. fi
  526. if [ -n "$header" ]; then
  527. if [ -n "$C" ]; then printf -- '\n%s\n' "$C <header>"; fi
  528. printf -- '%s\n' "$header"
  529. if [ -n "$C" ]; then printf -- '%s\n' "$C </header>"; fi
  530. fi
  531. if [ -s "$blocklistFile" ]; then
  532. if [ -n "$C" ]; then printf -- '\n%s\n' "$C <blocklist>"; fi
  533. cat -- "$blocklistFile"
  534. if [ -n "$C" ]; then printf -- '%s\n' "$C </blocklist>"; fi
  535. fi
  536. if [ -n "$footer" ]; then
  537. if [ -n "$C" ]; then printf -- '\n%s\n' "$C <footer>"; fi
  538. printf -- '%s\n' "$footer"
  539. if [ -n "$C" ]; then printf -- '%s\n' "$C </footer>"; fi
  540. fi
  541. }
  542. # Print to stdout if the output value is equal to -
  543. if [ "$outputFile" = - ]; then
  544. printOutputFile
  545. else
  546. if [ -d "$outputFile" ]; then
  547. logError "Cannot write '$outputFile': is a directory"
  548. exit 1
  549. elif touch -- "$outputFile" >/dev/null 2>&1; then
  550. printOutputFile > "$outputFile"
  551. elif exists sudo && exists tee; then
  552. printOutputFile | sudo tee -- "$outputFile" >/dev/null
  553. else
  554. logError "Cannot write '$outputFile': permission denied"
  555. exit 1
  556. fi
  557. fi
  558. logAction "$blocklistCount blocked domains!"
  559. }
  560. main "$@"