Last active
December 27, 2015 08:29
-
-
Save te223/7296679 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
r''' | |
青空文庫のテキストファイルを結合するためのpython スクリプト。 | |
python > 2.7 が必要。 python3 では動きません。 | |
使い方は、$ python bindao.py -h 及び、 $ python bindao.py -H | |
''' | |
import sys, os, re | |
import zipfile, tempfile | |
_version = "0.8.0" | |
_program="bindao.py" | |
#_isdebug = True | |
_isdebug = False | |
_ioenc = "utf-8" | |
######## Convenience Functions ############################# | |
def expand_path(path): | |
return os.path.expandvars( os.path.expanduser(path) ) | |
def is_emptyline(line): | |
return re.match(r'^[\r\n]*$', line) | |
def isterminal(f): | |
return hasattr(f, "isatty") and f.isatty() | |
def find(foo, seq): | |
for s in seq: | |
if foo(s): return s | |
return None | |
def U(s): | |
if isinstance(s, str): return s.decode("utf-8", "replace") | |
return unicode(s) | |
def MS(s): return U(s).encode("ms932", "replace") | |
def escMS(s): return re.escape(MS(s)) | |
def oprif(port, fmt, *args): | |
if not args: port.write(U(fmt).encode(_ioenc, "replace")) | |
else: | |
ustr = U(fmt) % tuple( | |
U(a) if isinstance(a, str) else a for a in args) | |
port.write(ustr.encode(_ioenc, "replace")) | |
def prif(fmt, *args): oprif(sys.stdout, fmt, *args) | |
def eprif(fmt, *args): oprif(sys.stderr, fmt, *args) | |
# for zipfile.ZipFile() in python2.6 | |
class FileCtx(object): | |
def __init__(self, fobj): self.fobj = fobj | |
def __enter__(self): return self.fobj | |
def __exit__(self, exc_type, exc_value, traceback): | |
if not (self.fobj == sys.stdin): | |
# print " %s will close" % self.fobj # for debug | |
self.fobj.close() | |
# print u" %s did close" % self.fobj # for debug | |
return False # throw exception | |
# examples: | |
# >>> fill("abc", 10) #-> 'abc ' | |
# >>> fill("abc", 10, "-") #-> 'abc-------' | |
# >>> fill([1,2,3],5 ) #-> [1, 2, 3, None, None] | |
# >>> fill([1,2,3],5, [0]) #-> [1, 2, 3, 0, 0] | |
# >>> fill((1,2,3), 6) #-> (1, 2, 3, None, None, None) | |
# >>> fill((1,2,3), 6, (0,)) #-> (1, 2, 3, 0, 0, 0) | |
def fill(seq, length, e=None): | |
if( e is None): | |
if isinstance(seq, basestring): e = " " | |
elif isinstance(seq, list): e = [None] | |
elif isinstance(seq, tuple): e = (None,) | |
for i in xrange(len(seq), length): seq = seq + e | |
return seq | |
######## End of Convenience Functions ###################### | |
# unicode でパターンを与えて、ms932 byte列の正規表現パターンを得る関数 | |
# eformat(fmt, arg ...) | |
# fmt には、正規表現オペレータ文字を ascii で指定 | |
# arg... で与えた文字は、正規表現エスケープされ、ms932のバイト列に | |
# 変換される。 | |
# Shift jis のバイト列のままで正規表現をかけるための苦肉の策... | |
# 全てutf-8に変換してから処理するのでもいいのかもしれない。でも、 | |
# ms932 <-> utf# のマッピングで文字化けを起こしてまうかな、と思ったので… | |
# | |
# examples: | |
# p = eformat(r'%s\s*(%s|%s|%s)%s', "表示起動", "大", "中", "小", "見出し") | |
# rx = re.compile(p) | |
# m = rx.search(MS("abc表示起動 中見出しxyz")) | |
# print m.group().decode("ms932") #=> 表示起動 中見出し | |
# print m.group(1).decode("ms932") #=> 中 | |
def eformat(fmt, *args): | |
return ( | |
fmt % tuple( re.escape( U(s).encode("ms932", "replace")) for s in args ) | |
) | |
# rep_midasi の utf-8 版 (デバッグ用) | |
# examples: | |
# s = u"start[#ここから大見出し]q[#大見出し]abcd[#「○○」は中見出し]dayo[#「○○」は大見出し]aaa[#中見出し終わり]rrr[#ここで中見出し終わり]" | |
# print rep_midasi_u(s) | |
# たぶん、[#「[#ここから大見出し]」は中見出し] なんてのはバグる | |
def rep_midasi_u(line): | |
def replace(m): | |
s = m.group(0) | |
ns = u"中" | |
if m.group(1) == u"中": ns = u"小" | |
return line[m.start(0):m.start(1)] + ns + line[m.end(1):m.end(0)] | |
line = re.sub(ur'[#「.*?」は(?:同行|窓)?(大|中)見出し]', replace, line) | |
line = re.sub(ur'[#(?:同行|窓)?(大|中)見出し(終わり)?]', replace, line) | |
line = re.sub(ur'[#ここ(?:から|で)(大|中)見出し(終わり)?]', replace, line) | |
return line | |
# examples: | |
# s = MS( u"start[#ここから大見出し]q[#大見出し]abcd[#「○○」は中見出し]dayo[#「○○」は大見出し]aaa[#中見出し終わり]rrr[#ここで中見出し終わり]" ) | |
# print s.decode("ms932") | |
# print rep_midasi(s).decode("ms932") | |
# たぶん、[#「[#ここから大見出し]」は中見出し] なんてのはバグる | |
def rep_midasi(line): | |
def replace(m): | |
s = m.group(0) | |
ns = MS(u"中") | |
if m.group(1) == MS(u"中"): ns = MS(u"小") | |
return line[m.start(0):m.start(1)] + ns + line[m.end(1):m.end(0)] | |
#line = re.sub(ur'[#「.*?」は(?:同行|窓)?(大|中)見出し]', replace, line) | |
p = eformat('%s.*?%s(?:%s|%s)?(%s|%s)%s', | |
"[#「", "」は", "同行", "窓", "大","中", "見出し]") | |
line = re.sub(p, replace, line) | |
#line = re.sub(ur'[#(?:同行|窓)?(大|中)見出し(終わり)?]', replace, line) | |
p = eformat('%s(?:%s|%s)?(%s|%s)%s(%s)?%s', | |
"[#", "同行", "窓", "大", "中", "見出し", "終わり", "]") | |
line = re.sub(p, replace, line) | |
#line = re.sub(ur'[#ここ(?:から|で)(大|中)見出し(終わり)?]', replace, line) | |
p = eformat("%s(?:%s|%s)(%s|%s)%s(%s)?%s", | |
"[#ここ", "から", "で", "大", "中", "見出し", "終わり", "]") | |
line = re.sub(p, replace, line) | |
return line | |
def srch_midasi(uline, report = None): | |
if not report: | |
report = {u"大":0, u"中":0, u"小":0} | |
for p in [ ur'[#「.*?」は(?:同行|窓)?(大|中|小)見出し]', | |
ur'[#(?:同行|窓)?(大|中|小)見出し\s*]', | |
ur'[#ここから(大|中|小)見出し\s*]']: | |
for m in re.finditer(p, uline): | |
midashi = m.group(1) | |
if midashi in report: | |
report[midashi] = report[midashi] + 1 | |
else: | |
eprif("ERROR: program error in srch_midasi - %s\n", midashi) | |
return report | |
class UngetableReader(object): | |
def __init__(self, port): | |
self.port = port | |
self.unget_buf = [] | |
def nextline(self): | |
if self.unget_buf: | |
return self.unget_buf.pop(0) | |
else: | |
return self.port.readline() | |
def eachline(self): | |
while True: | |
data = self.nextline() | |
if data: yield(data) | |
else: break | |
def unget(self, data): self.unget_buf.append(data) | |
class TextReader(object): | |
def __init__(self, filename): | |
self.filename = filename | |
self.headers = [] | |
def read(self, cb_header, cb_body): | |
with open(self.filename) as f: | |
reader = UngetableReader(f) | |
self.read_header(reader, cb_header, cb_body) | |
def read_header(self, reader, cb_header, cb_body): | |
for line in reader.eachline(): | |
if is_emptyline(line): | |
return self.read_note(reader, cb_body) | |
if cb_header: | |
if not cb_header(line): break | |
return | |
def read_note(self, reader, cb_body): | |
# 注記のセパレーター(--------------- )の読み込み | |
line1 = reader.nextline() | |
uline = line1.decode("ms932", "replace").strip() | |
#print "line1=", uline | |
if not re.match(r'^----+$', uline): | |
reader.unget(line1) | |
return self.read_body(reader, cb_body) | |
# 注記のtag 【テキスト中に現れる記号について】の読み込み | |
line2 = reader.nextline() | |
uline = line2.decode("ms932", "replace").strip() | |
#print "line2=", uline | |
if not re.match(ur'^\s*【テ.スト中.*記号.*】', uline, re.U): | |
eprif(u"WARNING: 記号注釈タグがありません\n") | |
reader.unget(line1) | |
reader.unget(line2) | |
return self.read_body(reader, cb_body) | |
# 記号注釈をスキップ | |
for line in reader.eachline(): | |
# 全角空白も strip されちゃう | |
uline = line.decode("ms932", "replace").strip() | |
if re.match(r'^----+$', uline) : | |
eline = reader.nextline() | |
if not is_emptyline(eline): reader.unget(eline) | |
return self.read_body(reader, cb_body) | |
#print "記号注釈:", uline | |
return | |
def read_body(self, reader, cb_body): | |
#print "--- body ---" | |
if cb_body: | |
for line in reader.eachline(): | |
#sys.stdout.write(line.decode("ms932", "replace")) | |
if not cb_body(line): break | |
# Zip から text ファイルを探して、その ZipInfo を返す。 | |
# zfile は、zipfile名を示すstrか、または ZipFile object | |
def find_ziptext(zfile): | |
def find(z): | |
for zinfo in z.infolist(): | |
if re.search( r'\.txt$', zinfo.filename, re.I): | |
return zinfo | |
return None | |
if isinstance(zfile, zipfile.ZipFile): return find(zfile) | |
else: | |
with FileCtx(zipfile.ZipFile(zfile, "r")) as zp: | |
return find(zp) | |
class ZipTextReader(TextReader): | |
#def __init__(self, filename): | |
# TextReader.__init__(self, filename) | |
def read(self, cb_header, cb_body): | |
with FileCtx(zipfile.ZipFile(self.filename, "r")) as zp: | |
zinfo = find_ziptext(zp) | |
if zinfo is not None: | |
with FileCtx(zp.open(zinfo, "r")) as f: | |
reader = UngetableReader(f) | |
self.read_header(reader, cb_header, cb_body) | |
class Checker(object): | |
def __init__(self, filename): | |
self.filename = filename | |
self.iszip = zipfile.is_zipfile(self.filename) | |
self.reset() | |
def reset(self): | |
self.body_count = 0 | |
self.headers = [] | |
self.midashi = {} | |
def check(self): | |
self.reset() | |
tr = (ZipTextReader if self.iszip else TextReader)(self.filename) | |
tr.read(self.mk_headerhd(), self.mk_bodyhd()) | |
def mk_headerhd(self): | |
def handler(line): | |
self.headers.append( line.strip().decode("ms932", "replace") ) | |
return True | |
return handler | |
def mk_bodyhd(self): | |
def handler(line): | |
self.body_count += 1 | |
#sys.stdout.write("%04d:" % self.body_count + line.decode("ms932", "replace")) | |
uline = line.decode("ms932", "replace") | |
self.midashi = srch_midasi(uline, self.midashi) | |
return True | |
return handler | |
def is_replace_midashi(self): | |
if self.midashi.get(u"大", 0) > 0: | |
if self.midashi.get(u"中",0) > 0: | |
if self.midashi.get(u"小", 0) <= 0: | |
#大分類があり、中分類があり、小分類がない場合は変換 | |
return True | |
else: | |
#大分類があり、中分類がない場合は、大->中 の変換 | |
return True | |
return False | |
def writeWork(outport, work): | |
#見出しのありなしをチェック | |
chk = Checker(work.filename) | |
chk.check() | |
is_replace_midashi = chk.is_replace_midashi() | |
outport.write( MS(u'[#改ページ]\r\n') ) | |
title = work.title or u"タイトル不明" | |
s = u"%s[#「%s」は大見出し]\r\n" % (title, title) | |
outport.write( MS(s) ) | |
subtitle = work.subtitle | |
if subtitle: | |
s = u"[#2字下げ]" + subtitle + u"\r\n" | |
outport.write( MS(s) ) | |
author = work.author | |
if author: | |
s = u"[#地から3字上げ]" + author + u"\r\n" | |
outport.write( MS(s) ) | |
outport.write("\r\n") | |
def body_handler(line): | |
if is_replace_midashi: | |
line = rep_midasi(line) | |
outport.write( line ) | |
return True | |
tr = (ZipTextReader if work.iszip else TextReader)(work.filename) | |
tr.read(None, body_handler) | |
def writeWork_plain(outport, work): | |
outport.write( MS(u'[#改ページ]\r\n') ) | |
def handler(line): | |
outport.write( line ) | |
return True | |
tr = (ZipTextReader if work.iszip else TextReader)(work.filename) | |
tr.read(handler, handler) | |
def bind(bindfile, bindtop, worklist, isplain=False): | |
with open(bindfile, "wb") as outport: | |
if bindtop.title: | |
outport.write( MS(bindtop.title) + "\r\n" ) | |
if bindtop.subtitle: | |
outport.write( MS(bindtop.subtitle) + "\r\n" ) | |
if bindtop.author: | |
outport.write( MS(bindtop.author) + "\r\n" ) | |
outport.write("\r\n") | |
writer = writeWork | |
if isplain: writer = writeWork_plain | |
for work in worklist: | |
writer(outport, work) | |
class TempfileCtx(object): | |
def __init__(self): | |
(fd, self.tmpfile) = tempfile.mkstemp(".txt", "bindao") | |
os.close(fd) # Fix me | |
if _isdebug: prif("tmpfile = %s\n", self.tmpfile) | |
def __enter__(self): return self.tmpfile | |
def __exit__(self, exc_type, exc_value, traceback): | |
os.unlink(self.tmpfile) | |
return False # throw exception | |
def bindzip(bindname, bindtop, worklist, others=[], isplain=False): | |
with FileCtx(zipfile.ZipFile(bindname.encode("utf-8", "replace") + ".zip", | |
"w", zipfile.ZIP_DEFLATED)) as outzp: | |
# write bind text | |
with TempfileCtx() as tfile: | |
bind(tfile, bindtop, worklist, isplain) | |
outzp.write(tfile, bindname.encode("utf-8", "replace") + ".txt") | |
# wite worklist's zipfile exception text file | |
for work in worklist: | |
if work.iszip: | |
with FileCtx(zipfile.ZipFile(work.filename, "r") ) as zp: | |
txtinfo = find_ziptext(zp) | |
for zinfo in zp.infolist(): | |
if zinfo is not txtinfo: | |
bytes = zp.read(zinfo) | |
outzp.writestr(zinfo, bytes) | |
#prif("mkzip: write %s#%s\n", work.filename, zinfo.filename) # debug | |
# write others | |
for fname in others: | |
outzp.write(fname, os.path.basename(fname)) | |
class Work(object): | |
def __init__(self, filename, order): | |
self.filename = filename | |
self.order = order | |
(self.title, self.subtitle, self.author) = (None, None, None) | |
if self.filename is None: self.iszip = None | |
else: | |
self.iszip = zipfile.is_zipfile(self.filename) | |
def ziptext(self): | |
"zipアーカイブ中のテキストファイルを ZipInfoで返す" | |
if self.iszip: return find_ziptext(self.filename) | |
else: return None | |
def text_filename(self): | |
ztext = self.ziptext() | |
if ztext: return ztext.filename | |
elif self.iszip: return None | |
return self.filename | |
def textname(self): | |
fname = self.text_filename() | |
if fname is not None: | |
return os.path.basename(fname) | |
return None | |
def set_default_by_checker(self, sub2title=False): | |
"sub2title: 副題(2行目)を表題とする" | |
if(os.path.isfile(self.filename)): | |
(self.title, self.subtitle, self.author) = (None, None, None) | |
c = Checker(self.filename) | |
c.check() | |
if len(c.headers) == 2: | |
(self.title, self.author) = c.headers | |
elif len(c.headers) == 3: | |
if sub2title: | |
(_dmy, self.title, self.author) = c.headers | |
else: | |
(self.title, self.subtitle, self.author) = c.headers | |
class CDoc: | |
def __init__(self, name, pattern, usage, desc): | |
(self.name, self.pattern, self.usage, self.desc) = ( | |
U(name), U(pattern), U(usage), U(desc)) | |
if isinstance(self.pattern, basestring): | |
self.pattern = re.compile(self.pattern) | |
def display(self, indent=0): | |
sp = u" " * indent | |
prif(u"%s%s\n%s Usage: %s\n", sp, self.name, sp, self.usage) | |
for line in self.desc.split("\n"): | |
prif("%s %s\n", sp, line) | |
class CDocList: | |
def __init__(self, *docs): self.doclist = docs | |
def find(self, s): | |
return find(lambda d: d.pattern.match(s), self.doclist) | |
_cmddocs = CDocList( | |
CDoc("show", "sh.*", "sh{ow} {all}", | |
u''' 現在の情報を表示する。 | |
Topレベルでは合本の情報を表示します。 | |
editコマンドで構成作品の編集中ならば、各構成ファイルの情報を表示します。 | |
all を指定すると、合本ファイルと全ての構成ファイルの情報を表示します。'''), | |
CDoc("title", "ti.*", "ti{tle} {表題}", | |
u''' Topレベルならば、合本の表題を設定します。 | |
editで構成作品の編集中ならば、構成作品の表題を設定します。 | |
表題の指定を省略すると、現在の表題を表示します。'''), | |
CDoc("author", "au.*", "au{thor} {著者名}", | |
u''' Topレベルならば、合本の著者名を設定します。 | |
editで構成作品の編集中ならば、構成作品の著者名を設定します。 | |
著者名の指定を省略すると、現在の著者名を表示します。'''), | |
CDoc("subtitle", "sub.*", "sub{title} {副題}", | |
u''' Topレベルならば、合本の副題を設定します。 | |
editで構成作品の編集中ならば、構成作品の副題を設定します。 | |
副題の指定を省略すると、現在の副題を表示します。'''), | |
CDoc("unset", "(unset|del).*", "unset t|a|s", | |
u''' Topレベルならば、合本の 表題|副題|著者名 を無効にします。 | |
editコマンドで各構成作品の編集中ならば、その構成作品の表題|副題|著者名 | |
を無効にします。 | |
(tを指定:表題の削除 aを指定:著者名の削除 sを指定:副題の削除)'''), | |
CDoc("edit", "(?!echo)e.*", "e{dit} 合本の構成ファイル名又は構成番号", | |
u''' 指定した構成作品の、表題、副題、著者名などを編集するモードになり、 | |
プロンプトが構成ファイル名に変わります。'''), | |
CDoc("top", "(to|ret|up|\\^).*", "to{p}", | |
u''' 合本の本体の、表題、副題、著者名などを編集するモードになり、 | |
プロンプトが、`Top> ' に変わります。'''), | |
CDoc("check", "ch.*", "ch{eck}", | |
u''' 合本を作成するための情報が足りているかを確認します'''), | |
CDoc("bind", "bind$", "bind {ファイル名}", | |
u''' 各構成ファイルを結合して、ファイル名で合本します。 | |
ファイル名省略時は、bind.txt。 | |
ファイルエクステンションを省くと .txt が自動で付きます。'''), | |
CDoc("bindzip", "bindzip$", "bindzip {合本名} {with 画像ファイルなど...}", | |
u'''\ | |
各構成ファイルを結合して合本テキストを作成し、with で指定した画像 | |
ファイルなどと一緒にzipでアーカイブします。 | |
zip形式の構成ファイルについては、そのzip内の画像ファイルは自動で出力 | |
アーカイブに組み込まれます。 | |
`合本名` -- 合本となるzipアーカイブファイル名(.zipは必要ありません) | |
zip 内の合本テキストファイルは、`合本名`.txt になります。 | |
合本名省略時は `bind`''' ), | |
CDoc("help", "(h|\\?).*", "h{elp} {command}", | |
u'''コマンドの使い方を表示します。 | |
command を省略すると、全てのコマンドを表示します。'''), | |
CDoc("#", "#.*", "#コメントテキスト", "コメント"), | |
CDoc("quit", "q.*", "q{uit}", "終了"), | |
) | |
def cmdhelp(cmd=None): | |
if cmd: | |
doc = _cmddocs.find(cmd) | |
if doc: doc.display() | |
else: prif(u"No help for %s\n", cmd) | |
else: | |
for doc in _cmddocs.doclist: | |
doc.display(); prif("\n") | |
def main(worklist, | |
sub2title=False, noauthor=False, isplain=False): | |
bindtop = Work(None, 0) | |
current_work = bindtop | |
inport = UngetableReader(sys.stdin) | |
for w in worklist: | |
w.set_default_by_checker(sub2title) | |
if noauthor: w.author = None | |
def tty(): return isterminal(inport.port) | |
def prompt(): | |
if(tty()): | |
if current_work == bindtop: pstr = u"Top" | |
else: | |
pstr = os.path.splitext(current_work.textname() or u"Unknown")[0] | |
sys.stdout.write(pstr + u"> ") | |
sys.stdout.flush() | |
def find_work(fname): | |
if fname is None: return None | |
def comp(wk): | |
tname = wk.textname() | |
return fname in (tname, os.path.splitext(tname)[0], | |
wk.text_filename(), wk.filename) | |
retwork = find(lambda w: comp(w), worklist) | |
if not retwork: | |
try: retwork = worklist[ int(fname) - 1 ] | |
except (ValueError, IndexError) : pass | |
return retwork | |
def show_title_etc(work, indent, *args): | |
sp = u" " * indent | |
for key in args: | |
if key == u"title": | |
fmt = u"合本時の表題: %s\n" | |
if work == bindtop: fmt = u"合本の表題: %s\n" | |
prif(sp + fmt, work.title or u"★未設定★") | |
elif key == u"author": | |
fmt = u"合本時の著者: %s\n" | |
if work == bindtop: fmt = u"合本の著者: %s\n" | |
prif(sp + fmt, work.author or u"★未設定★") | |
elif key == u"subtitle": | |
fmt = u"合本時の副題: %s\n" | |
if work == bindtop: fmt = u"合本の副題: %s\n" | |
prif(sp + fmt, work.subtitle or u"-未設定-") | |
def show_top(show_works=False): | |
show_title_etc(bindtop, 0, u"title", u"author", u"subtitle") | |
if show_works: | |
for w in worklist: show_work(w, 0) | |
else: | |
for w in worklist: show_work_filename(w, 0) | |
def show_work_filename(work, indent=0): | |
sp = u" " * indent | |
fname = work.filename | |
if work.iszip: | |
fname = u"%s 内の %s" % (fname, (work.text_filename() or u"")) | |
prif(sp + u"構成ファイル(%d): %s\n", work.order, fname) | |
def show_work(work, indent=0): | |
sp = u" " * indent | |
show_work_filename(work, indent) | |
show_title_etc(work, indent+2, u"title", u"author", u"subtitle") | |
c = Checker(work.filename) | |
c.check() | |
if c.headers: | |
prif(sp + u" 元テキストの表題、著者名など:\n") | |
for h in c.headers: prif(sp + u" " + h + "\n") | |
else: prif(sp + u" 何もありません\n") | |
prif(sp + u" 元テキストの本文の行数:%d\n", c.body_count) | |
#if c.midashi.get(u"小"): prif(u" ★小見出しがあります★\n") | |
prif(sp + u" 元テキストの大見出し:%s 中見出し:%s 小見出し:%s\n", | |
*( (u"%d件" % (c.midashi.get(k,0))) if c.midashi.get(k,0) else u"なし" | |
for k in (u"大", u"中", u"小"))) | |
if not isplain and c.is_replace_midashi(): | |
prif(sp + u" ☆合本時には、各見出しを変換します☆\n") | |
elif c.midashi.get(u"大") and c.midashi.get(u"中") and c.midashi.get(u"小"): | |
prif(sp + | |
u" ★大、中、小の全ての見出しがありますので、合本時には大見出しが重複します★\n") | |
prif("\n"); | |
def precheck(): | |
msg = [] | |
# 合本の表題、著者が指定されているか? | |
if not bindtop.title: msg.append(u"合本の表題が未指定です") | |
if not bindtop.author: msg.append(u"合本の著者名が未指定です") | |
if not isplain: | |
for w in worklist: | |
fname = os.path.basename(w.filename) | |
if not w.title: msg.append(u"構成作品 %s の表題が未指定です" % fname) | |
if not noauthor: | |
if not w.author: msg.append(u"構成作品 %s の著者名が未指定です" % fname) | |
return msg | |
if tty(): inport.unget("show\n") | |
#for line in iter(sys.stdin.readline, ""): | |
for line in inport.eachline(): | |
try: | |
if isinstance(line, str): line = line.decode(_ioenc, "replace") | |
(top, rest) = fill( line.strip().split(None,1), 2, [u""]) | |
top = top.lower() | |
if top.startswith("#"): | |
prompt() | |
continue | |
elif top.startswith("echo"): | |
prif("%s\n", rest) | |
elif top.startswith("e"): # edit command | |
if not rest: cmdhelp("edit") | |
else: | |
work = find_work(rest) | |
if work: | |
current_work = work | |
#inport.unget("show\n") | |
if tty(): show_work(current_work) | |
else: eprif(u"ERROR: %s は、構成ファイル外です\n", rest) | |
elif find(lambda p: top.startswith(p), ("to", "ret", "up", "^")): # return top command | |
current_work = bindtop | |
if tty(): show_top() | |
elif (not top) or ( top.startswith("sh")): # show command | |
if top or tty(): | |
if rest.startswith("all"): show_top(True) | |
elif current_work == bindtop: show_top() | |
else: show_work(current_work) | |
elif top.startswith("ti"): # title command | |
if rest: current_work.title = rest | |
if tty() or (not rest): show_title_etc(current_work, 0, "title") | |
elif top.startswith("au"): # author command | |
if rest: current_work.author = rest | |
if tty() or (not rest): show_title_etc(current_work, 0, "author") | |
elif top.startswith("sub"): # subtitle command | |
if rest: current_work.subtitle = rest | |
if tty() or (not rest): show_title_etc(current_work, 0, "subtitle") | |
elif find(lambda p: top.startswith(p), ("unset", "del")): # unset command | |
if( rest.startswith("t") ): | |
current_work.title = None | |
show_title_etc(current_work, 0, "title") | |
elif( rest.startswith("a") ): | |
current_work.author = None | |
show_title_etc(current_work, 0, "author") | |
elif( rest.startswith("s") ): | |
current_work.subtitle = None | |
show_title_etc(current_work, 0, "subtitle") | |
else: | |
cmdhelp("unset") | |
elif top.startswith(u"ch"): # check command | |
emsg = precheck() | |
if emsg: | |
for e in emsg: prif("%s\n", e) | |
else: prif("OK\n") | |
elif top == u"bind": | |
if not bindtop.title: | |
eprif(u"ERROR: 合本の表題が未指定です\n") | |
else: | |
bindfile = rest or "bind.txt" | |
if not os.path.splitext(bindfile)[1]: | |
bindfile = bindfile + ".txt" | |
bind(bindfile, bindtop, worklist, isplain) | |
if tty(): prif(u"OK: %s を出力しました\n", bindfile) | |
elif top == u"bindzip": | |
if not bindtop.title: | |
eprif(u"ERROR: 合本の表題が未指定です\n") | |
else: | |
bindname = u"bind" | |
others = [] | |
if rest: | |
restlist = rest.split() | |
id_with = -1 | |
try: id_with = restlist.index("with") | |
except ValueError:pass | |
if id_with == 1: | |
bindname = os.path.splitext(os.path.basename(restlist[0]))[0] | |
others = restlist[2:] | |
elif id_with == 0: | |
others = restlist[1:] | |
elif(id_with == -1): | |
bindname = os.path.splitext(os.path.basename(restlist[0]))[0] | |
else: bindname = None | |
#prif("bindname=%s others=%s\n", bindname, ",".join(others)) # debug | |
if bindname is None or re.match(r'''^\.+''', bindname): | |
cmdhelp("bindzip") | |
else: | |
bindzip(bindname, bindtop, worklist, others=others, isplain=isplain) | |
if tty(): prif(u"OK: %s を出力しました\n", bindname + ".zip") | |
elif top.startswith("q"): break | |
elif find(lambda p: top.startswith(p), ("h", "?")): | |
if rest: cmdhelp(rest.split()[0]) | |
else: cmdhelp() | |
else: | |
eprif(u"ERROR: Illegal command - %s\n", line) | |
except Exception as ex: | |
if _isdebug: | |
import traceback | |
tblist = traceback.format_tb(sys.exc_info()[2]) | |
for tbstr in tblist: sys.stderr.write(tbstr) | |
eprif("ERROR: %s\n", str(ex)) | |
prompt() | |
if __name__ == '__main__': | |
def filecheck(fname): | |
if not os.path.exists(fname): | |
eprif("ERROR: ファイル '%s' が見当たりませんので無視します。\n", fname) | |
return False | |
elif not os.path.isfile(fname): | |
eprif("ERROR: ファイル '%s' は普通のファイルじゃないみたいなので無視します。\n", fname) | |
return False | |
else: return True | |
def usage(): | |
progname = os.path.basename(sys.argv[0]) | |
prif(ur'''青空文庫のテキストファイルを結合するスクリプト。 | |
使用例: | |
青空文庫から、3つの zip ファイルをダウンロードして、それらを、 | |
AozoraEpub3 と kindlegenを使って、一つのmobi に変換する場合の例です。 | |
# まずは、青空文庫から zip ファイルをダウンロードする | |
$ curl -O http://www.aozora.gr.jp/cards/xxxx/files/some_textfile1.zip | |
$ curl -O http://www.aozora.gr.jp/cards/xxxx/files/some_textfile2.zip | |
$ curl -O http://www.aozora.gr.jp/cards/xxxx/files/some_textfile3.zip | |
# some_textfile[1-3] を結合して bind.zip を作る | |
$ %s some_textfile.zip some_textfile2.zip some_textfile3.zip | |
Top> help | |
コマンドの説明などが表示される... | |
Top> title 適当な表題 | |
Top> author 作者の名前 | |
Top> show all | |
Top> bindzip | |
OK: bind.zip を出力しました | |
Top> quit | |
# AozoraEpub3 を使って、epub ファイルの作成 | |
$ java -jar AozoraEpub3.jar | |
(端末設定 で kindle-PW を選択 | |
表題: 本文内 「表題→著者名」を選択し、「先頭が発行者」のチェックを外す。 | |
目次設定の「目次(NCX)階層化」のチェックをいれる、他はデフォルトのまま。 | |
bind.zipをドラッグアンドドロップ) | |
# mobi 変換 | |
$ kindlegen ......... | |
Usage: %s {OPTIONS} text_or_zipfiles ... | |
text_or_zipfiles: | |
合本される個々の作品ファイル (zip または 普通のtextファイル) | |
OPTIONS: | |
-sub: 個々の作品ファイルの副題を表題とする | |
(たいていは、シリーズ物を合本するに使用) | |
-noauthor: | |
個々の作品ファイルの著者名を省く。 | |
(たいていは、同じ作者のシリーズ物を合本する時に使用) | |
-p : 階層的な見出しにしない | |
(デフォルトでは、構成作品の表題を「大見出し」に設定し、 | |
作品の本文中に現れる、大見出しを中見出しに、そして、 | |
中見出しを小見出しに変換する処理を行います。 | |
-p オプションを指定すると、この変換処理を行いません。) | |
-ioenc ENC : 端末入出力のエンコード (defaultは utf-8) | |
-ms: -ioenc cp932 と同じ | |
-V : バージョン番号の表示 | |
-h : %s の 使い方を表示 | |
-H : コマンドヘルプを表示 | |
''' , progname, progname, progname) | |
exit(1) | |
isplain = False ; sub2title = False; noauthor = False; | |
workfiles = [] | |
arglist = sys.argv[1:] | |
try: | |
while arglist: | |
arg = arglist.pop(0) | |
if arg.startswith("-"): | |
if arg == "-p": | |
isplain = True | |
elif arg == "-sub": | |
sub2title = True | |
elif arg.startswith("-noa"): | |
noauthor = True | |
elif arg.startswith("-H"): | |
cmdhelp() | |
exit(1) | |
elif arg == "-V": | |
prif("%s %s\n", _program, _version) | |
exit(0) | |
elif arg == "-ioenc": | |
_ioenc = arglist.pop(0) | |
elif arg == "-ms": | |
_ioenc = "cp932" | |
else: | |
usage() | |
else: | |
workfiles.append(arg) | |
except IndexError: | |
usage() | |
if not workfiles: | |
usage() | |
worklist = [] | |
order = 1 | |
for fname in workfiles: | |
if filecheck(fname): | |
work = Work(fname, order) | |
if work.iszip and (work.ziptext() is None): | |
eprif("ERROR: アーカイブ '%s' の中にテキストファイルがないようですので無視します\n", | |
fname) | |
else: | |
worklist.append(work) | |
order += 1 | |
if worklist: | |
main(worklist, sub2title=sub2title, noauthor=noauthor, isplain=isplain) | |
else: | |
eprif("処理するファイルがなかったので、何もしないで終わります。\n") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment