Tuesday, September 20, 2016

Emacs 25.1 & hunspell

In Emacs 25 you don't ever need to modify ispell-dictionary-alist variable explicitly. Ispell package reads, during its initialization, the hunspell's .aff files & automatically fills the variable w/ parsed values.

If you have at least hunspell + hunspell-en-US dictionary installed, the minimum configuration, that works regardless of the underlying OS is:

(setenv "LANG" "en_US.UTF-8")
(setq ispell-program-name "hunspell")
(setq ispell-dictionary "en_US")

hunspell cuts out en_US part from LANG env variable and uses it as a default dictionary. To check it outside of Emacs, run:

$ hunspell -D

If it says


& waits for the user input from the stdin, then perhaps hunspell was configured properly.

ispell-dictionary on the other hand, is an Ispell-only setting. It uses it to start a hunspell session.

Iff the Ispell package has been initialized correctly, ispell-hunspell-dict-paths-alist variable should contain pairs like

("american" "/usr/share/myspell/en_US.aff")
("british" "/usr/share/myspell/en_GB.aff")

& ispell-dictionary-alist--the parsed values from the corresponding .aff files.

If ispell-hunspell-dict-paths-alist is nil, that means Ispell is either has failed to parse the output of a `hunspell -D` invocation or has failed to read the .aff files. The latter could occur if you use a native Windows version of Emacs w/ hunspell from Cygwin. If that is the case, you can always set the pairs manually:

(setq ispell-program-name "c:/cygwin64/bin/hunspell.exe")
(setq ispell-hunspell-dict-paths-alist
      '(("en_US" "C:/cygwin64/usr/share/myspell/en_US.aff")
        ("ru_RU" "C:/cygwin64/usr/share/myspell/ru_RU.aff")
        ("uk_UA" "C:/cygwin64/usr/share/myspell/uk_UA.aff")
        ("en_GB" "C:/cygwin64/usr/share/myspell/en_GB.aff")))

You'll need to restart Emacs after that.

The Apostrophe

If you can, try up update the hunspell dictionaries alongside the spell checker itself. The old versions lack the proper WORDCHARS setting inside the .aff files which results in wrong results (haha) for words that contain the ' sign. For example, if your dictionaries are up to date, the word isn't must not confuse the spell checker:

$ echo isn\'t | hunspell
Hunspell 1.3.3

If you get this instead:

$ echo isn\'t | hunspell
Hunspell 1.3.3
& isn 9 0: sin, ins, ism, is, in, inn, ion, isl, is n

the dictionaries are no good & no Emacs will fix that.

Switching Dictionaries On The Fly

If you find yourself switching dictionaries depending on the Emacs input mode, use the Mule hooks to set the right dictionary automatically:

(setq ispell-dictionary "en_GB")

(defun my-hunspell-hook()
  "Set a local hunspell dictionary based on the current input method."
  (setq ispell-local-dictionary
         ((null current-input-method)
         ((string-match-p "ukrainian" current-input-method)
         ((string-match-p "russian" current-input-method)
          (user-error "input method %s is not supported"

(defun my-hunspell-hook-reset()
  (setq ispell-local-dictionary ispell-dictionary))

(add-hook 'input-method-activate-hook 'my-hunspell-hook)
(add-hook 'input-method-deactivate-hook 'my-hunspell-hook-reset)

Saturday, September 3, 2016

inn 2.6.0, Injection-Info & .POSTED

I usually refrain myself from writing anything in the style of "How to setup foo", for it's quite silly, but my recent adventures in gmane forced me to break my rule for there is practically 0 info about the inn+newsstar "stack".

The setup is:

  1. Fedora 24.
  2. We create newsgroup gmane.test in an INN installed on a localhost (this INN is lonely & isn't connected to any other NNTP servers out there).
  3. We use newsstar (grab the .spec file from here) to connect to news.gmane.org machine, download articles from remote gmane.test group & post them to local gmane.test newsgroup.
  4. We use our favourite newsreader (Mutt + nntp patch) to read local gmane.test newsgroup.
  5. We post our message to local gmane.test newsgroup. Then we run newsstar again & it uses INN's spool of messages prepared to be sent away. newsstar grabs our message, connects to news.gmane.org machine & posts it.

In reality, the last step could be the hardest one, for after running newsstar we get the response that out article was rejected by news.gmane.org w/ the cryptic reason:

441 Can't set system Injection-Info: header

Let's begin w/ the

Step 1, creating the local newsgroup

After dnf install inn, run:

# systemctl enable innd
# systemctl start !$
# /usr/libexec/news/ctlinnd newgroup gmane.test

Step 2, configure newsstar

If you have built an rpm from the .spec above, copy a sample config

# mkdir /etc/newsstar
# cp /usr/share/doc/newsstar/sample_config/main.cf.sample !$/main.cf

& uncomment the lines corresponding to the default INN paths:

spool_dir       /var/spool/news
active_file     /var/lib/news/active
outgoing_dir    /var/spool/news/outgoing
articles_dir    /var/spool/news/articles

Next, create /var/lib/newsstar/newsrc.news.gmane.org file, add the desired remote newsgroup name & set the proper file ownership:

# echo 'gmane.test -1' > /var/lib/newsstar/newsrc.news.gmane.org
# chown news:news !$

Run newsstar under news user:

$ sudo -u news newsstar

It should download the last article from remote gmane.test group & post it to local gmane.test newsgroup.

In case of errors, look into journalctl & run newsstar w/ -vv CL options.

Steps 3-4, posting

Open /etc/news/newsfeeds & add the following lines to it:


Restart INN (systemctl restart innd).

Open your newsreader, post an article to gmane.test. It obviously immidiately appears in the local INN installation. Now we need to push it to the remote gmane server.

$ sudo -u news newsstar

& boom--newsstar says it moved the "bad" article to its graveyard, for gmane didn't like it. You may open the buried article & examine its contents. There is 2 things in it that forbid us from pushing it upstream.

  1. Injection-Info header.
  2. Path header that has something like .POSTED.localhost in the middle of it.

To make life more enjoyable, INN doesn't provide any obvious way to either not to set Injection-Info header nor to edit Path properly. The only way I found is to use INN perl (yes, it's that bad) filters. It's a little more challenging to do it under Fedora, for the maintainer of inn package had simultaneously decided (a) to compile INN w/ perl support & (b) to turn it off by not including a sample (filter_innd.pl) filter in the package (this is why INN cries in the logs that perl filters are disabled).

Grab the INN tarball, extract filter_innd.pl file to /usr/libexec/news/filter/ (just creating an empty file won't do) & mark it executable. Then open filter_nnrpd.pl (be careful, this is not the same file we've extracted from the tarball) & add to filter_post subroutine:

$hdr{'Injection-Info'} = undef;
$modify_headers = 1;

return $rval;

Restart INN. At this point, this change to the article generation is enough for such servers as news.eternal-september.org, but if you post another article to gmane.test & run newsstar, the reply from gmane still fails to comfort:

441 Path: header shows a previous injection of the article

To satisfy gmane, we need to change Path header from:

Path: my-machine.example.com!.POSTED.localhost!not-for-mail


Path: my-machine.example.com!not-for-mail

There is a setting for /etc/news/inn.conf, called addinjectionpostinghost that reduces .POSTED.localhost to .POSTED but it's still not enough. Again, edit filter_post subroutine in /usr/libexec/news/filter/filter_nnrpd.pl to add:

$hdr{'Path'} = 'not-for-mail';

Restart INN, repost the message, rerun newsstar & go jogging in the park, because, congratulations, dude! you've wasted an hour of your life for nothing.