wordgrinder-0.5.1.orig/0000755000000000000000000000000012251160512011663 5ustar wordgrinder-0.5.1.orig/COPYING0000644000000000000000000000204211523516526012727 0ustar Copyright © 2007-2009 David Given Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.wordgrinder-0.5.1.orig/Makefile0000644000000000000000000001624412251157336013344 0ustar # © 2007-2013 David Given. # WordGrinder is licensed under the MIT open source license. See the COPYING # file in this distribution for the full text. hide = @ PREFIX = $(HOME) CC = gcc WINCC = mingw32-gcc.exe WINDRES = windres.exe MAKENSIS = makensis ifneq ($(findstring Windows,$(OS)),) OS = windows else OS = unix endif VERSION := 0.5.1 FILEFORMAT := 4 DATE := $(shell date +'%-d %B %Y') override CFLAGS += \ -DVERSION='"$(VERSION)"' \ -DFILEFORMAT=$(FILEFORMAT) \ -DPREFIX='"$(HOME)"' \ -Isrc/c \ -Isrc/c/minizip \ -Wall \ -ffunction-sections \ -fdata-sections \ --std=gnu99 override LDFLAGS += \ WININSTALLER := bin/WordGrinder\ $(VERSION)\ setup.exe all: unix unix: \ bin/wordgrinder \ bin/wordgrinder-debug \ bin/wordgrinder-static .PHONY: unix windows: \ bin/wordgrinder.exe \ bin/wordgrinder-debug.exe \ $(WININSTALLER) .PHONY: windows wininstaller: $(WININSTALLER) .PHONY: wininstaller install: bin/wordgrinder bin/wordgrinder.1 @echo INSTALL $(hide)install -D -m 755 bin/wordgrinder $(PREFIX)/bin/wordgrinder $(hide)install -D -m 644 bin/wordgrinder.1 $(PREFIX)/share/man/man1/wordgrinder.1 $(hide)install -D -m 644 README.wg $(PREFIX)/share/doc/wordgrinder/README.wg # --- Builds the script blob ------------------------------------------------ LUASCRIPTS := \ src/lua/_prologue.lua \ src/lua/xml.lua \ src/lua/utils.lua \ src/lua/events.lua \ src/lua/redraw.lua \ src/lua/document.lua \ src/lua/forms.lua \ src/lua/ui.lua \ src/lua/browser.lua \ src/lua/html.lua \ src/lua/margin.lua \ src/lua/fileio.lua \ src/lua/export.lua \ src/lua/export/text.lua \ src/lua/export/html.lua \ src/lua/export/latex.lua \ src/lua/export/troff.lua \ src/lua/export/opendocument.lua \ src/lua/import.lua \ src/lua/import/html.lua \ src/lua/import/text.lua \ src/lua/import/opendocument.lua \ src/lua/navigate.lua \ src/lua/addons/goto.lua \ src/lua/addons/autosave.lua \ src/lua/addons/docsetman.lua \ src/lua/addons/scrapbook.lua \ src/lua/addons/pagecount.lua \ src/lua/addons/widescreen.lua \ src/lua/menu.lua \ src/lua/cli.lua \ src/lua/main.lua .obj/luascripts.c: $(LUASCRIPTS) @echo SCRIPTS @mkdir -p .obj @lua tools/multibin2c.lua script_table $^ > $@ clean:: @echo CLEAN .obj/luascripts.c @rm -f .obj/luascripts.c # --- Builds a single C file ------------------------------------------------ define cfile $(objdir)/$(1:.c=.o): $1 Makefile @echo CC $$@ @mkdir -p $$(dir $$@) $(hide)$(cc) $(CFLAGS) $(cflags) $(INCLUDES) -c -o $$@ $1 $(objdir)/$(1:.c=.d): $1 Makefile @echo DEPEND $$@ @mkdir -p $$(dir $$@) $(hide)$(cc) $(CFLAGS) $(cflags) $(INCLUDES) \ -MP -MM -MT $(objdir)/$(1:.c=.o) -MF $$@ $1 DEPENDS += $(objdir)/$(1:.c=.d) objs += $(objdir)/$(1:.c=.o) endef # --- Builds a single RC file ----------------------------------------------- define rcfile $(objdir)/$(1:.rc=.o): $1 Makefile @echo WINDRES $$@ @mkdir -p $$(dir $$@) $(hide)$(WINDRES) $1 $$@ objs += $(objdir)/$(1:.rc=.o) endef # --- Links WordGrinder ----------------------------------------------------- define build-wordgrinder $(exe): $(objs) Makefile @echo LINK $$@ @mkdir -p $$(dir $$@) $(hide)$(cc) $(CFLAGS) $(cflags) $(LDFLAGS) -o $$@ $(objs) $(ldflags) clean:: @echo CLEAN $(exe) @rm -f $(exe) $(objs) endef # --- Builds the WordGrinder core code -------------------------------------- define build-wordgrinder-core $(call cfile, src/c/utils.c) $(call cfile, src/c/zip.c) $(call cfile, src/c/main.c) $(call cfile, src/c/lua.c) $(call cfile, src/c/word.c) $(call cfile, src/c/screen.c) $(call cfile, .obj/luascripts.c) endef # --- Builds the LFS library ------------------------------------------------ define build-wordgrinder-lfs $(call cfile, src/c/lfs/lfs.c) endef # --- Builds the minizip library -------------------------------------------- define build-wordgrinder-minizip $(call cfile, src/c/minizip/ioapi.c) $(call cfile, src/c/minizip/zip.c) $(call cfile, src/c/minizip/unzip.c) endef # --- Builds emulation routines --------------------------------------------- define build-wordgrinder-emu $(call cfile, src/c/emu/wcwidth.c) endef # --- Builds the ncurses front end ------------------------------------------ define build-wordgrinder-ncurses $(call cfile, src/c/arch/unix/cursesw/dpy.c) endef # --- Builds the Windows front end ------------------------------------------ define build-wordgrinder-windows $(call cfile, src/c/arch/win32/gdi/dpy.c) $(call cfile, src/c/arch/win32/gdi/glyphcache.c) $(call cfile, src/c/arch/win32/gdi/realmain.c) $(call rcfile, src/c/arch/win32/wordgrinder.rc) src/c/arch/win32/wordgrinder.rc: \ src/c/arch/win32/manifest.xml endef # --- Unix ------------------------------------------------------------------ ifeq ($(OS),unix) cc := gcc INCLUDES := -I/usr/include/lua5.2 UNIXCFLAGS := \ -D_XOPEN_SOURCE_EXTENDED \ -D_XOPEN_SOURCE \ -D_GNU_SOURCE \ -DARCH=\"unix\" \ -I/usr/include/ncursesw ldflags := \ -lncursesw \ -llua5.2 \ -lz cflags := $(UNIXCFLAGS) -Os -DNDEBUG objdir := .obj/release exe := bin/wordgrinder objs := $(eval $(build-wordgrinder-core)) $(eval $(build-wordgrinder-ncurses)) $(eval $(build-wordgrinder-minizip)) $(eval $(build-wordgrinder)) cflags := $(UNIXCFLAGS) -g objdir := .obj/debug exe := bin/wordgrinder-debug objs := $(eval $(build-wordgrinder-core)) $(eval $(build-wordgrinder-ncurses)) $(eval $(build-wordgrinder-minizip)) $(eval $(build-wordgrinder)) cflags := $(UNIXCFLAGS) -g -DEMULATED_WCWIDTH -DBUILTIN_LFS objdir := .obj/debug-static exe := bin/wordgrinder-static objs := $(eval $(build-wordgrinder-core)) $(eval $(build-wordgrinder-ncurses)) $(eval $(build-wordgrinder-minizip)) $(eval $(build-wordgrinder-lfs)) $(eval $(build-wordgrinder-emu)) $(eval $(build-wordgrinder)) bin/wordgrinder.1: wordgrinder.man @echo MANPAGE $(hide)sed -e 's/@@@DATE@@@/$(DATE)/g; s/@@@VERSION@@@/$(VERSION)/g' $< > $@ endif # --- Windows --------------------------------------------------------------- ifeq ($(OS),windows) cc := $(WINCC) WINDOWSCFLAGS := \ -DEMULATED_WCWIDTH \ -DBUILTIN_LFS \ -DWIN32 \ -DWINVER=0x0501 \ -DARCH=\"windows\" \ -Dmain=appMain \ -mwindows ldflags := \ -static \ -lcomctl32 \ -llua \ -lz cflags := $(WINDOWSCFLAGS) -Os -DNDEBUG objdir := .obj/win32-release exe := bin/wordgrinder.exe objs := $(eval $(build-wordgrinder-core)) $(eval $(build-wordgrinder-minizip)) $(eval $(build-wordgrinder-lfs)) $(eval $(build-wordgrinder-emu)) $(eval $(build-wordgrinder-windows)) $(eval $(build-wordgrinder)) cflags := $(WINDOWSCFLAGS) -g objdir := .obj/win32-debug exe := bin/wordgrinder-debug.exe objs := $(eval $(build-wordgrinder-core)) $(eval $(build-wordgrinder-minizip)) $(eval $(build-wordgrinder-lfs)) $(eval $(build-wordgrinder-emu)) $(eval $(build-wordgrinder-windows)) $(eval $(build-wordgrinder)) src/c/arch/win32/wordgrinder.rc: \ src/c/arch/win32/icon.ico \ src/c/arch/win32/manifest.xml $(WININSTALLER): extras/windows-installer.nsi bin/wordgrinder.exe @echo INSTALLER @mkdir -p bin # $(dir) doesn't work with spaces $(hide)$(MAKENSIS) -v2 -nocd -dVERSION=$(VERSION) -dOUTFILE=$(WININSTALLER) $< clean:: @echo CLEAN $(WININSTALLER) @rm -f $(WININSTALLER) endif # --- Final setup ----------------------------------------------------------- -include $(DEPENDS) wordgrinder-0.5.1.orig/README.Unix0000644000000000000000000001124312251157265013501 0ustar WORDGRINDER V0.5.1 ================== © 2007-2013 David Given 2013-12-06 Unix version INTRODUCTION ============ WordGrinder is a simple, Unicode-aware word processor that runs on the console. It's designed to get the hell out of your way and let you write; it does very little, but what it does it does well. It supports basic paragraph styles, basic character styles, basic screen markup, a menu interface that means you don't have to remember complex key sequences, HTML import and export, and some other useful features. WordGrinder does not require X. It runs in a terminal. INSTALLATION ============ WordGrinder needs the following packages installed: - ncursesw, the wide-character version of curses. This is supplied with most systems. - Lua 5.2, the programming language. This is available from: http://www.lua.org/ - zlib. You will already have this, but just on the offchance, it's here: http://www.zlib.net/ - LuaFileSystem 1.2, the file system access extension to Lua. This is available from: http://www.keplerproject.org/luafilesystem/index.html - a terminal emulator that supports UTF-8. gnome-terminal, konsole, xterm and rxvt-unicode do. rxvt and the Linux console don't (unless you use jfbterm). You will also need a decent set of Unicode fonts or WordGrinder will look funny. If you have Debian, you've already got all these. Merely install these packages: libncursesw5-dev liblua5.2-dev lua-filesystem-dev zlib1g-dev To build, simply do: make To install, do: sudo make install PREFIX=/usr (Change PREFIX to whatever you like; naturally, if you're not installing in a global location, you don't need sudo.) It is now ready to use. USAGE ===== Do this: wordgrinder ...to get a blank document. You can load an existing document with: wordgrinder README.wg Please read README.wg; it contains the manual. There is also a man page, which describes the command line interface. If you use WordGrinder, please join the mailing list. This will allow you to ask questions, hopefully receive answers, and get news about any new releases. You can subscribe or view the archives at the following page: https://lists.sourceforge.net/lists/listinfo/wordgrinder-users LICENSE ======= WordGrinder is available under the MIT license. Please see the COPYING file for the full text. WordGrinder contains a copy of the LuaFileSystem code. This is also MIT licensed and is © The Kepler Project. See http://www.keplerproject.org/luafilesystem for more information. REVISION HISTORY ================ WordGrinder 0.5: 2013-12-06: Major overhaul: fixed hideous file corruption bug; much improved Windows text renderer; bold; page count; widescreen mode; UI style overhaul; many other minor bugfixes. Many thanks to Connor Karatzas for extensive Windows testing. WordGrinder 0.4.1: 2013-04-14: Minor bugfixes and build optimisation in aid of the Debian package. WordGrinder 0.4: 2013-03-24: Major overhaul: OpenDocument import/export, new much smaller file format, a proper Windows port, updated to Lua 5.2, switched away from Prime Mover to make (sob), much bug fixage. WordGrinder 0.3.3: 2009-12-13: Fixed a bug when searching for or replacing strings containing multiple whitespace characters (that was triggering the crash handler). Thanks to lostnbronx for the report. Added RAW and PRE paragraph styles. Cleaned up HTML import. Add customisability to HTML export. Relicensed to MIT. WordGrinder 0.3.2: 2008-11-03: Fixed a very simple and very stupid typo that caused a crash if you tried to turn autosave on. Added a simple exception handler to try and prevent data loss on error in the future. WordGrinder 0.3.1: 2008-09-08: Minor bugfix revision to correct a few minor but really embarrassing crashes problems in 0.3: no crash on HTML import, no crash on File->New. Also some minor cosmetic fixes I noticed while doing the work. WordGrinder 0.3: 2008-09-07: Lots more bug fixes. Added LaTeX export; troff export; next/previous word/character; table of contents; autosave; scrapbook; Windows console port. Fixed some issues with key binding. Lua bytecode is now embedded in the executable, making it self contained. --lua option. General overhaulage. WordGrinder 0.2: 2008-01-13: Lots of bug fixes. Added word count. Added about dialogue. WordGrinder 0.1: 2007-10-14: Initial release. THE AUTHOR ========== WordGrinder was written by me, David Given. You may contact me at dg@cowlark.com, or visit my website at http://www.cowlark.com. There may or may not be anything interesting there. wordgrinder-0.5.1.orig/README.Windows.txt0000644000000000000000000001013712251157305015022 0ustar WORDGRINDER V0.5 ================ © 2007-2013 David Given 2013-12-06 Windows version INTRODUCTION ============ WordGrinder is a simple, Unicode-aware word processor. It's designed to get the hell out of your way and let you write; it does very little, but what it does it does well. It supports basic paragraph styles, basic character styles, basic screen markup, a menu interface that means you don't have to remember complex key sequences, HTML import and export, and some other useful features. Note: WordGrinder originated as a Unix program and as such it does not behave anything like a traditional Windows application. You Have Been Warned. INSTALLATION ============ Run the supplied installer. It's done. You can switch to use a different font with the drop-down menu from the application icon in the top-left corner of the screen. Important note! To quit, you'll have to use the menus inside WordGrinder (because the application knows nothing about the Windows window close button). Do CTRL+Q to quit. You can toggle full-screen mode with ALT+ENTER. USAGE ===== Run WordGrinder from the start menu. There's an option for the menu. Please read it, as WordGrinder doesn't work like other Windows applications. Press ESC to get the menu. Press ESC, F, O to open a file. If you use WordGrinder, please join the mailing list. This will allow you to ask questions, hopefully receive answers, and get news about any new releases. You can subscribe or view the archives at the following page: https://lists.sourceforge.net/lists/listinfo/wordgrinder-users LICENSE ======= WordGrinder is available under the MIT license. Please see the COPYING file for the full text. WordGrinder contains a copy of the LuaFileSystem code. This is also MIT licensed and is © The Kepler Project. See http://www.keplerproject.org/luafilesystem for more information. REVISION HISTORY ================ WordGrinder 0.5: 2013-12-06: Major overhaul: fixed hideous file corruption bug; much improved Windows text renderer; bold; page count; widescreen mode; UI style overhaul; many other minor bugfixes. Many thanks to Connor Karatzas for extensive Windows testing. WordGrinder 0.4.1: 2013-04-14: Minor bugfixes and build optimisation in aid of the Debian package. WordGrinder 0.4: 2013-03-24: Major overhaul: OpenDocument import/export, new much smaller file format, a proper Windows port, updated to Lua 5.2, switched away from Prime Mover to make (sob), much bug fixage. WordGrinder 0.3.3: 2009-12-13: Fixed a bug when searching for or replacing strings containing multiple whitespace characters (that was triggering the crash handler). Thanks to lostnbronx for the report. Added RAW and PRE paragraph styles. Cleaned up HTML import. Add customisability to HTML export. Relicensed to MIT. WordGrinder 0.3.2: 2008-11-03: Fixed a very simple and very stupid typo that caused a crash if you tried to turn autosave on. Added a simple exception handler to try and prevent data loss on error in the future. WordGrinder 0.3.1: 2008-09-08: Minor bugfix revision to correct a few minor but really embarrassing crashes problems in 0.3: no crash on HTML import, no crash on File->New. Also some minor cosmetic fixes I noticed while doing the work. WordGrinder 0.3: 2008-09-07: Lots more bug fixes. Added LaTeX export; troff export; next/previous word/character; table of contents; autosave; scrapbook; Windows console port. Fixed some issues with key binding. Lua bytecode is now embedded in the executable, making it self contained. --lua option. General overhaulage. WordGrinder 0.2: 2008-01-13: Lots of bug fixes. Added word count. Added about dialogue. WordGrinder 0.1: 2007-10-14: Initial release. THE AUTHOR ========== WordGrinder was written by me, David Given. You may contact me at dg@cowlark.com, or visit my website at http://www.cowlark.com. There may or may not be anything interesting there. wordgrinder-0.5.1.orig/README.wg0000644000000000000000000003255412251160346013175 0ustar WordGrinder dumpfile v2: this is not a text file! xua{γyb 11O|Y.Lā0CMNtL9}/%j#ڨCMϺ[ V 9UoU9^nVj!;լoʅD-eZXRaK+5z:"꺗WQFFY UpjC0!o"*y\atzRf@7_3uys^O]]iw(AhFBpR B2h|),p` /ү:b8\?g3PͰjtxJpZ fXčZTƦJ evXl7J $g3bkp!s}1 ᖺ畃R]be"=KE襃/ϙu eN79 'JwR_ #Q9\ ȩv@C[W9~u ]AN%W51s !\oiRFt)0_yܩI9䏧;4]L ]"kn4QO8XH5(>)1{pFP)/MR&BU#5l5}1e HhFQ߫ 3\*va1r\G9Y-] B1E#eЭ+cV'dԎP„/MWnz5L p.+Rih%vr1SbTCqQt|z^GA\*s"@NHiTYu#ŭ޺p%:pFuCDZ8 =o$Ժ<$U; A&K,hsRHnDOGuj63#R -hYCѯVEh$yhE!OeX1swsr08B &+c)c?JZVN:kIo, EI?ME-l7L Km@0\$!::~ 7\FjT_0L~C }Yu&Dgp(DQ2KA8#!Wj`kJ%l?ڔ3)ZBe|}T\7 "EU"H_ A!\%U 25-= VJQ-7kY8MT+ẼNc:fv]U61IoyMliDr&.<ltvNȼ_`lRAWc*xE)c 1eq7v9u~Rp(vƠh;r[ǃy+Mm7^= hf,%IKl(kZ4^~ɯSo7u6P0QgH`: rB| ;'0,R1h ef`36 nz2m˘+2[?DiM8D5^O j&,g7=_͜8(^f=)b(;&L"`bL=(a5WQlKd lr\ f ٛY vnƿ08/n5j0 OJl ԮrM ~-,")]YGȃo,ofx'{-iˀfHݎ} eO_lE{'Í1 ۄ_r'1n,6' 7[\+2d2N}rvMqhA"~PĻ}@= i~sut޼|KӠ7wAVF,=A9,$Q+ 6 *,9ٚ=6p VPwTop~ⷿv5;5+ESCoÀ1Epm4դ>yYv@6*ƒO}7l>>o<=@?ò$Q {\PVDBU,bfyg06J+"NKp̔r| M;j.@Hl֠f-8[٠,+cN f=oQ#HX |na26PT!\@QB(YDgRT(8jJC'5l auvs9TPȲ͆Q7.p>Xgi{D hl@ɮz augkf:vQmΠ7hՄQ= t#zexbܑ0NTlDGoq$YN$&G˿s#߹iM9ظ_nC6-C`~p:(QW][O GR*B`ảm/ t; ~0׈L@7 :^-$ac|x,>ī,_Edb?6:󳶒"uie<쿹;a`ʠphbwtzFhlBZuT1{a!_,Y$_1p;-<nM /aGqXT\PՠUFezL5@bH-ҩER3 y,q?sx'|Mt̛9![3pZһqp#C0wI',ĖT2[($v5-Oa-B >Gksiæ&alI@H$?-uܨ7#o3UEqByA2f-blQI(o z@5 ۲? uo!^Y2[3CV6s $$(e0z3R8.8䌧7Z̸D<$/)mW,]d>{$q|:jnFfqe(4mS$|YbeI]]b]3eE2r*2J:"$+EIT ~s2bUo{'ksZL'ك* 0!j#Vr&p:PVTasR`l~~cߕ}6}/}@ZTpRɌ*PRRsC|.6BUJH DaL}Ldkf̅en# R %g9PҾ0ʀNXbv ?@#wr)%/ u3@{Y+AD6 1hDO靜#6b0`ȯR a:,{I5+3l ,4] mq7?<[!,(L:ZD> Dut~esCe5*ؼoC g`ɤ :K;͟7wUr@Η,,7Zr)ױ&pcS̨J92Ո$ Ioy,u3ż,uۇ3LNڋ+R{{# q Hr1ŜA-8ei|Zr%^]O/w)(æ6$cr,*>  _qo^"_rDpH @|m_AFMdoQ=YK*E@6^p-FrPQcsGW0; _K]v|PZ;n,yK҃>IOשZ9ы|;˱ő%2撯99./ "ya/$D.g^ț Qʳ%^G%fR2ԹYvƆwџ@hzpi ( }ʐL78j>q5d+W1&>玶? A:@_@PPM 9pDJ AX #xRرXT T,1*DAL= !@"])wW2 'C(=)OhmI@)T%u+<si ٗTTgfX/Xs1{?y>+f}۲ jkh.p"ԫS3+ T83,bFB}P7`@I:.yƃ;WVv5׼K]U=@[cg*Nh) c`Mk9zC}%|\zWt[T4Df:ϯa/.2sm#FE:*D4kBB:9Z/F; j(5*A kMB,ښDh%75z@%P砆V dN*גJK쩆>uH3Q&b5&cpSeqM\% F!Q .Sa=RAQ3߃vR;5DR1 ;n~S)5w"#ܳFϘ'7JH0?=!Ip}K%Ĭ;WUt_x)̓`ٱc$h4akA5&xW$Xf[QEj#( TUb5cz.lYCjDO\}9e5qwwl?Ib}XI*\W ֹDu|dFf$oZ;:[X pJQx-N, 1bxXݓeV03%nKMRSzM嬨6_"jpn2si# qu@1&W0H#'aru$[ nk54lC%yV(4'zvԆhzE\5'CV%P *"Wru͢T~ex]祯b8e?z}K & bC尾t"BpKҕqv}dڰ",J€ >LqPf[:!BYKpիt>7\M:2[*pwiJ,Pٻ̵S}QqvZy&AM{*eKD0zT F KB^wCθY\lzc0@)D,Y_5ԸD[4JHpb5%sAdV 3R8;$3=˛Rn D of%ZdBb5R-Q~,3W\ṾN^.\ 8&mqO[c =?<8i6L Z֬7)%+Կ:4LsYۼgv4{mf)Ė"6 g+ š ףj}E m4P̲)#=_g>9'ǟO?g?9>3gÎ ">}>!}E(+gbF{=Dh"RM:Jz 8?gU6eD%r t a5KCr~A$!6NrpF&,vh=AdQ xw;a_Gymaƚ7{gnKR4x$pwQcJ|Ι+w!`Or$x2K'Zy$h1~It837 toʋ11QBɶ.+YWfM$'LO{[<@u6"$o=+N~z: g! w~u6 m'x6lG$z.NJ#/t* P4:;ӿ}\ KڃD72 +I/(JuUUjyxZz{;hbkTMl X5g9&{Yq3$SR1ƑnY?fL?`-sr5`9.2 i*n*)𭐉K:cp/;7)xqewĘm l?/>ftAwN<֫ ~98k0_*+v(\s@*_LE֋INbz5Ro+|c Xxټ(a4!#+'~čmZRaOiEA6 L7~_"t0" `0E!C{j Xx0&Z;{hNq14eX+ŏ#!s1}uBUQ5ʒB.Nz[X0trUhb Z6G-F7%FU'.j̓j=*$,r1Dmp'YeJzVw o}ÖFO-[O)\- U %/]Cy;nBlnov- \'|g02齟43.j?I&\7_yC0lrƑDnZ*%zw; 6ڵȣcK {]$xfV^P}u~ͼU޹|W~s㺁lr9͐9JZ[ 2:c}3kX f\4 xu y}ylubmR) )qoLۆfRKkeAcĠ _Q g-B׷RP wL_[^鵶;7 (Hjx.GC3ns|`ZE/0KtmځymZP#BG aua%kW&J޹(1c`4ŸB8pA`S=u ʯ"^ &B0yMA)I#TfD#1zW8V4BJyEd/΃@oX[iN'5 S_Lz|a"~ip92w|1=7^IjI:5iScݨ_ݷ:0CN bt35+ 7q/\_b}PݛQ+?Y =eԱ5Qph80?XX"qY|K"6LHgȦ! DXLx  kL$UK꥛Y7LdC!/}ȎP)C7^=`#`VC)tG X4D :v4p#߽r6FΖWRLmhFqn&ABkG_d5Rr[ūɲ;,=%Rׅt46DG:jgס`] v&/#S/"8ɝzӧ-WKϟyN=wz۩O so{Nf>}[ P1o`xr>SW^QO7E$zD *-q^iH+} v:&JK0\<9M̫wz#A |8 h>=N▢2Fjކt1L󁞎Z"Lo`P8e3ZhNBo1Ks9Y,6e,ݥ1F9XeHBt|+: (DޞK:H3E lWhOMSD:>M7[QI<71ID/Vfm5p$\[1WG<LY.*At#`!Z~ uHBҚ|5IkM>ޚ<CdkHCldk?ٓn4!I:Tn#En3r0= RӂD>6Pu'LE*٥o%D ً?qG_hm;mwٿ~h㖏hm1Y7'zWRѲt@B01n`o!4+o Jp O[8_R-/W2?\?SЙyu5LңɄh`T_Zᩂ,5_8Yx3[=cȧ{NwԮ00;<-l3(5BIJd)H9 fceJCcQ<ܜ:uֱ4Ւʶџ>:zS{ZGm}^Ggzkղ0 sO2|^@z(5(I1)е 8pxV;ljLs@(doTӻJt7Wp}aa@0@cfN(\-<4:nO.h0 l>L~5y~6s\Iv}4c5,l^4hfܸT9zj oey5*y-&&lnNzRj>g@3A5s6O7rHOO^h. m>FQO60L#Z--fuR_j{jXV)]CA*ĉAh)/L(V1KȌხT J4OErz uz b "r(t奘qDГ)45)E>?ۉ[d?@-!]Q m#<3y3%~E^[I*--y%2xw,K|t9T PQ5},ueڞϻfe *㻫਍5{\^. ^լ*+k}7^5WQY57ryk]Õ5߿w۴ra힧X5=^_μ˽{ ^ǪorKB} { - / cvZz-y(: ){n[MSѪ[ŧ\2%t9xqެQKwD Vu'/ysNv@Ush˗^ow'WPfm{ T=%|frA e\Tʏ̙Եbwordgrinder-0.5.1.orig/extras/0000755000000000000000000000000012251160511013170 5ustar wordgrinder-0.5.1.orig/extras/icon-broken.xcf.gz0000644000000000000000000042052411523516526016542 0ustar ui'lhmagh-̪tt;Y C7tU';?ouSx"~s^M]`0C /`}0{^; ^z5>w={o| t70l|߯LMM~hsnmC{TufV&=OaO}hnamjeujbmnqa}jzfbʇ>u*>23>/| /oo> _S4610/~Ч`Sh[ӫSky}_0 9!fa&f(~relauzq2!j~wٽ=y돟1Uh@ ۅ3{]p@1S̿\Wx c0+Q/#e'q0yYKVƭ5ZAJ%X"F%r4 |+J@gɑ(b~hu&)NfId\O̤Rḩ1JrF1xG R'1/\D&` Rf+2L8? 1BbTr)!ViZϋ2Ǒ )%R\P#UOk5BX(Jj \\@(e< [|}BTTFe+Jd(:9_S)"ՋLyr/H`5D erL?MPSRX$d NdpXU!ID P,ȅ\F 2 B0l!,R eB&QLf t*ʅ, ֬֠fZ!y!WD\RcWnJXi6*B-rAPB<[^6(z>(KedN:]Πl$p]BT0RӍv1^4Bi5Z~7r\"*v3"JbyGl<اݫFP+ |IJd:NKr.SHfz57N= d2qO(73L1o4]l7{~6xMT{rީaҹL`/7 ~::l0_g҉~5_huT>V_Z.VVȵ*0Zj nhY̤rOv)7ZPt`ZvHo*bQ/6[G+UݻٯTȯ0=)6`VTЭ%Gz+Zg>Fq #geL+ k Hd2*`VdvmeB,/"Xd|> A6HOX:k;& L`R.776db, L'-2Dڼq}d77 ABFcP8 $9&DRصUԶ'R-Aa)X<%˨'noT 'it6B7Fg6V\U2BbF.$Kpx68F (&NabtJQIX C[nV"O"QCarl6|mb$*`u,b4 rm&Wn,-or鄵UbBh<ǛX2 G&c,0) A`!gpt"SD}= %0dXDvkscf ;|>`dnkGȔuE'F&bL0 Lbh|{Z#$ԭ͍E2h1;/Xr(!1`8.?:ZUKjL6K0 &fG\103=ĥ}#ãc/_057; #6#3.][X\E6=?qitbz|ass3cWQSc33c&o,NN\:=7} ]z呫3#''f`KܮN̍6}Ĺ+ӳcWƮ-_ɞ{|iչ˗'fߘ!G&f49wm~r|jrrkS/ ։+cOLϏO_k3} ^2:=CM^:>>Es/\<>y'ϟY:|9<;=9v:sjgg',_:3?61?>==ִ5 <977>z+#G j9.M]\ }`S333#`?yYap,G2 t Wg'H &aVWg/\Qw_b 5Id 殃/O-ON*` J^NϡutٺP<*ۥI'OaNd*^L:A&Y|!nq8_1  Ca0IK46F%NG^4 O[Y#1T6AfRIȀ\"t:GHK$,@nYi  GSL:-rHCv"RqB%Ҹb.J7OJ[ZLd9TL\#RXt agX*D$8R?Oi,D@^_Vg7T %baj+XKKdAdț84wW+QD2i$J*eO- Od]SKX 2CoXjs CBA Ykv<ʓ)b>l@A?;BTxkDJ3d j@bjRG[|) @bY$cLP(81&<,`':C$l5'o8ןr\{?yɽ֓0?N9> ﷝9˿ wNށ߶O@~sa;RL^>ȏa{.$?<^Q 2 4g?w&ΚJ㳎f9ÿ?9ǟ<ƞ5~}8>#{aɻ|.c oȯ:昃[+l2+2C ש0_ZlBcER$|\)(7i5PT)Tc>D,0jL!S+4LS̱=O*$B'D|>0TJ-ѡd rܨ0yE(W CTjF)VhTrR!"yV+Qb X|Z`͓q$1*X(sDJB! EJHL),dS{0?Q`PJ_eB# D?J}Ќy'fHyK.:Y+S"JH<&D24ƕG06W" L dq:T2Eb%$s΋uzJ.S1oLP*sX(W T&zBƭӐKeb婤" bNeZ3Uxlˁ$(: ]-Mxjojcvp|c5+/],TDSpR` 0B  wn?*Axkōz1l4 DKvV&Z&V}lK9;5ov{wn?MtxTH(& #[-2W/4jv?iuR6a F:˔ҩB37%y6:çwvw[ݝjL3K&vnn;3>)L )N{>;`ttۯTKxP#fR3(zfuNV^7hۻpzHDHPjlqdB9k˕nݾ78ym X"W-ҠeTftPۻ{vݽ`{ =_,ZB1c<8 }xio;OΠQ(d:laQo~Ѭj{Jhpmm߼uwpk^5חVQeWql_<w[T %FY :@`hą52a}$Cgh\ B"ysmuNH$*A#H4UK ln)D<ؐ~g2)x,͢VWg3CQ [ =J86FPxkmHu2.Kd1IxFeR6Dlu_%.-m.TH?1ߤq$*]^M"tdL:jN@=N! 2DRt&JX6xe%*_&'ԡF`84B"38I3m>BgsPZ[aJeQ6|ci,*zp8),"/O `*$*-'Xd"š/n``-:E%cWqUH퍕ue2u @A_ae"A1rcicE}W6з8Xhm ] iX*% FRbQ76qխ{@Zd˦ix:c8~c7L6C2eN%r8:}:_6),mAY^p$2esyc8΢ fcZ|[! +KVIl6'Љx" ĭu`&J&n\[wel\[b)te2x6u KXǢFdRb&W*$n kx M:mkL7i8& EoR):~ ܎c8 r6]luPv#R : AK"l-[7n.$h4:qmfZ͖[( F6W(,BZW(ťu2qy@9Lh3U#TQ%Qux1  's,_!/~q4 5m\R)WK9<&= !NX2@M .ŦQV/-n,bU+w;jD3poj=!ӮlUo\_^]YQk6Ht`!5)KXrl$+P._Pt|.$5AIU 7:I_2O:,?-1[]^h z̞^ >FdPx܅L,J`e*Zӯ.4CN&=vͪSr66Y<8nUbXj4Xk28|c*Nn NTkw]jچ&խsH$/f@3*\!HK,JfYݮHШM2v&LkYHɅX0N[B>WUt7*;7;@ep<ޘrDn6{bbV2R% pt+r6Ri{"1Oe y|fwۨY7U,Qo5˹ZR`+Y4Ja|t2a,Fl0X-mH^1,L! x"&t$}J!kbf]! U®D.&Lo"v\D4 X Esd8br</)Uh*U,wȹs-.;>9c#篠ʽs׮M_?wil??U?yNݘ?nln eމsFF2196y+W& M~ ϟ26>1:<;#cG/^8q|nnNM LM\Ew}:>15:{ձ.DG.S(^C_\697qultrfnǯ,./N=qq…yH_:91~+W'/M]Ay <=118;;}ubffz~nnjɉK+O> {ʻWGǦFǯNN28 ]f'??2>xc5wGWGΟcMMN^pԕ3SSSsWpyjnvfµqȕɹ''ff'¶]G&Giwљ٩)آcW.^<:1:,1vetdl|vܓc3o/>s~Qv/_<7Ktʅ+ccW?yyzfiuqx`adžt ff y_6?9=7SOOM,N_~lً̍v~vavjrd̵kTm^S.= &'./,L_0xmqzbW/{eq Ϗ\>㗠K:01yeraʼnׯ]>w#3#'.L6@Ysz3g`k/]G+L]~$lya3m^]xitrrlb{xzvodS_G} 0~ yfwyԅ'/ CȂ]ZkvEs^8XX(<tBBMHKS #aS3W/=9 ڂBk7Lj hCIG3YL:p^&%4I'6193 1tܒPl^ #D E>EP7-:GX]veoWk,v(ZtۜV1VW(4` :N_[^R}eQ6|a1̶!ۃ&L\ &]&7o.L LBkM`ש &3$vl[\^/J,,1T@xbp>V+FmFn5ft2u*`K \؂Wn;H`l.d.7$0H+d6 h1C  8cu}RZ%"UN_¥`3 $r.G*dLO"aLX;ۜdrz<^ա,!U)<>t%bS !_(U"IJ atVV4mN؄uLAN^B}+ǹ!>tr^Ȳo:4ӐYrr%W!>rʯՐ:y!K^o= r.S>trEW }]xOr/}M^ƻ!|'!k|e_4œW~W<|WR߈bNz7_ܣOc2P g@|+$8, g rrg1&⿝߂AA|{ȆVׇ1)WDP-Tw*HV5jZ璅;b:HWz#ZQެk\"]Ez^vrqGsv ۃʅb;.3N,H&p>SMNjVcf.YkN\dJ(rC2dʥ'.4.wRfPJ׊NDÑl]jtt^b$jy6z|2)ӱT>mwlU V'jlDJ3BUZ qo2 [J&TrHUjDPjz:j^5 嚍|t;L\i՚bwhub^ך8:3Rd+fGJr+lWw߬ڵ](ٸ4+)T5t4^ l6rZ9+#BjL J?~^5 Hۭ TW? ~0?daVVj4=8+o;fizȧT(haa^9~34++j.ej~ :P~gPOG ùB~NJYoJtR,WLW!t Sp\ne\r:;{o``jQ)F? TZܿR/ūzZkj^+.W2TAt4_(uە",&B!]փ(|6r$JYol;#@ݨz\I,CvL+Hpyԃj*|Op,Wlb1R&$n.UGJZQzǯ CkT>Uj\6B1WJۯq栕Kv?üsd7R)o*1I:ś\}5v_~=0^_~&\ xK'C;FS|H/J(v.VS @F{mz |^B>Y5*x!ml{X"E®@:OgRJ K=&9O<^-rZ[^7׀o7s (mP j*]*bc0 soXtQPwqg48dFӹn\)"Ѥ]7څt>\\ikHJs8C|YJ&ϏϥˍzK8r> @"~}NfK^%O53|/@7] Z.ꃱ~2\ 4T$#b86_OBsLh?#*` :?j|ik@ݭ}T~f&klXnϛH$ ~pzf+0&" ܎h.fVY}A;L^ 7K%Q3N%=^ZBn'JCiܝ%>w4ID`5TCv ka>o{@0sg^r!@%t{F@ z}6tg(ԫ :EJ~S7٣ %|.rufQeYLHGI\J{:慣=MeM۬x;9Jɀ;:RVndkmT'U.DCL=A‰$`<;Չ aq7ᒨ\T͚p:s4&` EJ'[рJՆTEJKJ̕JH!!?,7*\,dAJ@Q ǽ\,~ AaD}p Y`=PeIϛy6/FUgG" LFCy&>պ qU0 B.Bi2 w,}qB_28~W`08 6Y+?豚a)qm6EtAl <ՋScsۭ6hӡOF %d/Ϝ`o8p5&GA(oSqaPk> 2~xǣ->ln.:@K|?^QЙ sD^ފk<._'I!CxoW{y4%a d:tb0DgªW,H:9j{c f7| t&[ I}6%WRYSy|`5b:9PS Fu|d*FT6K4$i;%>\H:鳨bɠ7k}ވe3}ƈj ^lyˊǁt>% y v/-rvџJ|Sm"Z^:әUF?)}J3xyqD21ɪ1_x񞇧&?+%!vt! /*6ư$]oAd7#C[IH~DliOiŤ捆Acx#ID{!^Ik? |NX [e*!=*PZ݁D*S|fE5SZjŧrard@Ц uju. =3l5B _NdSрDX$HL@iیH筐#Ag TjUHn]P"~dßuJgWFO(Q' y#(]v3x`!w( Ē@(N-_yn{왔w*4js$SeL!lӍ*9DKmH=(0;\F%8 DDܢxl:kƢc7,DjXd"n,P(|Sd&d&`2h,"œ*dJ1xx aiK3TTg0Hp2F2M YBtn J'Er6Ժӳ9zǔ0%v-L2rTΧx, <.̖q[\PbґI]Z`(ׯ XѪva]hT2.:{y6ˣxtSH $"S7GĦB>Em1eb.N |CG4:q~< {}e 9_ bsdSDő ['dDH b=!CH94"\Se "#nx,8T"bzE6Lǭ,R\6g)L},=巃S#P!7M_L \'~g1k,!K#n$"Sx }}ȤD\6/_O|:nc@Y <$Sd"~y0ZzxAD<G,H 8r)e$ǾpUHL#+čM2B'W7D:A'Sez#Kk8Ϡ [t6GRWס-'29 ї^ zFǭhao58"'zL"H\~SJXB(nla~&gP֯38,6ŠllQ:Ƥfu{p bI5 `qRe[ȅg-~|ɛkx%dR1en<.F#kls[kD&vŗQc 7\SȀk4>n'qd"f~a !i" #n\Z%s|Py[D,E\X <*ţ |#ў1BIԍemsLd֦hϷ76I\!FbʱlƠlI8=_%u,Ck+XI3>2[Ud0<[cXx)O!sH<1' $( RzQ7n,np[[FIG (@c+4" sd\>hϛ\Al:f#`?;=JoYmbiu,c/)}pisho|TBpBOغ~~[!!gGfI4 _wL%D{Ec0@r<:D98߈hhˁ\a8,x1߽O#?hDP hO%De C%De0<o5MeI7K|u~I!oBi"VgaKck`A{N@~cL0x^$=oS/W 'ggA~sUWNHљ-cr$~p&}O>_As=㭓{1(;Ϝ'!?8(ѓ!.o| /wb C|oɵ_|HD3G0=0߮:3?VN/؋ ߂3H~.u&/a̽iOߠ>OWV̽?t{! N>SׯNxOrˊaRSKt1>H |c7LҬ7Z]jH +~-nBk1zPl{`5Я?JysPtrέ]pN))U ݝZ7N|P7Fl4r-fygtʍvw[+5;pAQ~P[dέNkp9+ͻw;,zpvBh֛^(r٩+]bhjwo޺}gP-cD>DvXw:ݛwoMtNZ?}sYjV҉BNz3lPr;>};;{GNoՁ#{Ơ[./4ѭD`J$Vdc坝?|=hwoݽ߿uwU*Je b-ml{ޭۻ/vwpgӮr# ҁX!Wt:hג;wu[݃`] mp엲6XV b5~oû{w#~>!̷ZYoת\ (b&k;G{OwvoM8y߿ٿuy++Z*jD2K]0& Ý;7#h_I!>rB&J}`ӷv;^O8Ԋ::۾P ;*̧ VA:<4ڽV}0 -b֬5x&?QVې[Nw闊;_0H-ur& ݧ!ۍN权f?humݬH}Ab hSAy[ W^ت^ ٷ<:8oީ6uZݽnQQ_u mB{u}f:x޽C|T?*bMv;WqPZ9~Vito,~ڀ xoBϔgl`ثV>mg(,$k`BiᄚMAۇH7[ht2pz t<{i>ww|. 7{LAo}dV} wݣGJX0jVz֠xRgw駟TTM3tuvwЮj2Ua?ջ۽r1Z,eK]ջj`އ5v;fwJQ LuZY=X۪W+mH3_vU4^̀A ) 4nTd RYUv?i4zy8ODzR˕w>NmMmV+=}[HNCwm nWrs{(B8D2nst_SwvK١3^87DmӇSH&w_t8bL*D\,/TZ9vzX*fsy sy:fNT|x>ϧLTdrr>cZU*ۻ4,č&_Ლm@KXՅ|\+cr 4ZHfk,]h,4ZS1W03D8J҉x";s%LCCH*NW!?Iba-L)Ţ@0 T<OjCӉD|&4otbSx^DmPe4Z`*\6CެJA32PVNEt` zA.n2͡U|+sjƋ|+MA3)4@"g2Zf0\mCB!S*6u`*Gal:wmucvH\ KUI'pġU}^OF2SgsjJgsx.K՛D ̷*E'ͥbpdiuf_ ]w;N(VstYNg*:o, &t(JH9ҕ"Vggw:L|R/e^;'щ"oKKT6-JrI]+W+ kHy4q3]3-ڝ.hjf5M^It>n1"].0^\nӪ=P{׶rZr[x. aL*Th_ :`L^gY RwϟVpܑlLJx,cͦlJ[Ӫ`)e"\1,]Hеi{.r}UpKɡ2qa._&>=znmr㙡O 5(L6w2U_-.WGb>CZ*ZӮe!3|2Ao+` jk8 FYxmym;*#N{: n8^VLFt*h]ondBj*"x j61kML"Fmٌ6u0].Ev#ErR)k6F|ZѪ#(HɪP\J2#ظ-ne՝=j;Ȗ*RQN G|aӻa]zaWL<^˥ 5[е:|qtLjIcZ)J^Xo2BnTdT<7uE( gT j$XkEnMsl@~@J.̢ OCoCMGHb4iFP(a.Τt6,lw{iQ6X X7˦6{4|wϮs{PRW"Z>u|PœyѨG5YFćcV "0SiB Jtc63೻I/5L"dht<!рAkq: u*;2 wZÉl!#z=^0lh )8En5P_0vcr^L:t db+m\ZuJ!J93P º@\*1Wi$P-*ͪ(,VM)Rxo2ZPt"m9l&AH P)46`vdYR<^ 'JxLF.Px<+jDx ,q8fEwhLKREƳ\7h5V^e`6 fQ=@wWlG3N#Y[ٍ*V#U:Mt3rB(p,._ݜd TH$<A 6S jPi ,rZPiT )T(Π"- 6M;:pA/d߸_bIT2%qp0G 1v .cJԶC?-<a 4E$ObJAku=.ߠ،2` tHSE<&B_k@Plmc slrS@W@‘HGչ5:CHݚrvb`'C:cI@$f0뫛7,N `<ŁSDW:\^4AYpup8\&N0חVhT>*k|Zlp꛰_aB؍M"j ֨w:՘^crqlKDYZ2<2].1<^d׻NN7bI!z=DhE5wmz5[絚Jf4rXl0m&E5! V -66d;vo|5st"E6;~ Fm2X=:dJ߄,^/RgBGcRpk$>SJ3X<= VޟDEc3$z9KR$ nH%jm FU#'ob@.1C%ML6wxf"ᗗpTS &g(.EћM`k-P+P]@$@аkx&7*&#dx qQa ahP|Dc+ֱt‹vE*QnAA-1Bg1Y4:F6=}>%+SF`ULtPj!Yī,6`M$`n%*#`s%VO9bX0CT_>9]a;4ÿ{r][P0'/'A|}9bLħN@l!HA@|'86'ǿ>ׁq|xGOO@ 6ć@|6ļǤ*^tOleBRz7b]nJW/ߨ1xQ/&+~k{?VW8O1?3_9TΕ Z4\*FrQI_L*mbIRZT"~gUOd0?-fVlu|3%^(;j%i?rt*xv0_`urW%7z;.o7 .:T|r3☿+¼BNijxC~ OT_E3kЪ8}Ko堶G}nty W0F;h<I v߯5"}Z[̾\Fm^;u!+=2iwRy$V} {=|H v# |v( Q "Mv|~ן{lTOӺt4up/U>U#~wәMF}voʏ, F:24% 49.dQ(: mw#E~Kc]Q8[[KZq2#=<,`0Hx<ͤ+4[\6 Ps~ܤ7R!4:NCfz[P'K<y/@QO'29D%R `d4 )M˥|$>z6'"ϭ2(lDȣ\>OYiNnV@!`z&2,hk)_\HDH,62dXGHS$0w,g7Nu ̽'|s)zOrY+]좇|ʟ8yɟa^ysߟx[y } ɫѻ}s~t2t7{aZ'g~t;{v脦Nh\--d{BκAa̝ÛRʠ_"osv{7% pxߏKlIso :[wtf?oW;wvwo>:x[]\XnF权:g{}tp>s!ibuثo>;{JשڇwݽgZT7 #tvݪvڍ.ʗ1=lW [wnvۭͧ9Aף|{xkQ>=߮4zXYTpohzJ4L`<`&tv9{5;7v0-l&f%_΅2~{{j춫C0.:Uq]UhU^eͣ~۪^=8=QLTUX֭>r5W = oAwP+Uvxkl7z:wܺiH[(nw;9nz;Jp=b8څ%`Rqgo;߄C fna{jsϽqrxtttGwڭFlnAv o ~v><jdp{`#tk+ gڃݝL[vw ϞyVhotf@lwn97ٿ=8{s{e{{/cE̯O0G;VkP;7>Bp3 bLakq9Vo(fJdTY+ UJcJM1hrkDVf2J >I;>ƳT&kVVN2b+6|R/w%h _q[CLo Vke _׬*N^BjPWO)_HcFfS*WZ֩gO/2؝^H˹xީʥ|2IӹZQG FEhs,J&b8HAMZX"WDZ)*T2]T@Tt.KSl$|nXNf /U-jf11Mv*R P(VQZ>Kcn tZ|2rxבAX:ös@@Ie 'f,NrV,\Ϥ2_pEKXdzh^NAKi"2FǶ[KrmxXeS;YzMQ+vS0Iv߫T& ]|aH RjZKRZX@lD+FD\2L@˕L$Td._VMJ+*h,G?OV|0~*Vrsgb&]iBOXӬ x7hb`P*VP!_L^#͡o*Wr;~A-sc8lV,Žr\݄7N.ZJ&_ V$R2V)dh5I_Anq,KB:_~ej | iX"!O`E`*i?Cж4dBo4;]nd5lgA?qR nqA]{-}yfl./lw /!z_9$5aw{=ŗ#zG 춢 &kYuR㴛M]!ɂB~Ha 8|^ 96IR++5&o@CgS x˨5Z)ZRI'~Fk"-  PZCѮ+mޠ˄jl}ݣkPthau}V͐7` D!/b  Xpg*`v9@](2>r6; ʨw z)3|?'KN{ŕw>ɻO?.r3~*a'89.]tz߂?N>9{'BE_x׀q}S(zK`{{*z?wS(| _yo7^A3Eχ?:65N|/PA<1Uh066^fxA=1)P߫uq=|vlS1¼(VZQ5Lz FmalrB*)ʅ =ST"FTȥB!W|u [)ΖB`H B.(JfUQ+_RV b\((\Y܁/AO2LɥeR~>D4 TҨPPrGᤒӇ֪2h8@ UH8gJTlHȑIl:[(=1jV yBDRe _ U"QU̝> Q.)Ö+|+i\ `S  ` 5#I,U,U3X,PRMi\ȑ4DMf*-dX`64P籅H>@2J)lTEr>Kf=P+2V%SJ_*jhpT2!OU+$ Z&J\E#؋3 J\UtZ]g;S0$@"%".Ggh:N'*v*@Ql\Lije"H W tk߱BȄ"ܠb6rZ) 'NB2MQ)E"qehyF)b=Nk6)rV ,N;C!"P $RJ -}Z#` 4b0&Lg]OR /I)Wߐ 9B!?^4\<>WKˑX.KXeP-$B~#SLPCH Y'K^ U: !KU)(%⨜S5RXꀽ*-y/IUHdrBql@.)IJR)[T=B#|HG&PyF#s v/r>P/B+㳩XHch5v~#拕2G"SkUrN# INo(k)||ӯ!d#"$"$1L&lNgqy|h!Y!bNhd"d[4*24 \G,ᙡ 9nkm"S)-:DePeSdRt&ekHPT" DQ+J 2C`IX 3ml2JИ؅MvX GFO4"q+08 (,hA!66W6!#n`qx 08,%~)q&:uemDl)"%*S vaj:nڍ5*CuyG!sYx:numkseH3ègkqAEiЈ80>E|BgP)@: H IG36ˢH2'vJ]_`4"LcYT"'<}8i`ل-:yeu,GoɨB_6oHd6 c՛;͞( -ᾷE66W*>G } I>,//Hp (M=67I8O%̍"GqDU ʤŽ jmeJpd,aй5:M^Pl.[@_^ X,mt mXm+=FZm mteB%6&۵:gt~\b7%K$ G17׷*]+@c0{<]Z|θc(b6 yL KI&¤V3lGoэzń i eJ S,\k Jx1;isDz 1}ė\`MV ;pX>D!kk4QzX*8P!Py@"20;;wmye~vڍk.. ^^>7}ur~fltbj~橙y#WLMON\e̍bdxZfG'&'fGF&f&Ff^wirrt|xrjbd%<}qx75>=s D٧GfG\tq _|+#sWFF]^EY MLM ĝ6ONϏO^8>uū0)po3##Oo7?{exrūscW<0dԕ:urꥉL*Ư^19=xcvb=~j“&RX0wKs#ŵKR9gzF&`^[\^XPrtp~]g.MݘDf,c[X>cѐԕѧ.L._CML TjJBcg禧mΎͬ\BH iL \i ]_]~\::F"X[><}mvb }yV6WWȠ,+#'OύO1).B4h}~z饅#sTe´^5c#ũdG\<(܍f&N._|ajwט[\ mWb0x3/~r򽆅JU4{XU<~'{2tr;c@w=3a>>{gOtgB~&n~w#39;gr<{ ƙxXǟ=y/g"w&~uz㏞3>?>{Nޏĥ38;#'@[gB{&=O>? k~&]>Uht˘_UsD*m F-MƼBǽq &@43P(5r]H7c?y;tOk^nIgd^.[1'1C0/|ʷNQ,롛'S[0L1.T|̋^Pnuj}P ^m9??SL ϡ;}. ZDRFP)F^ kA[*E+dZ)(ʅ\2MRA~ԛi  ]B}B:He3lLCYꏀ߬zftst D2UA5h5KGՆc`_7B=X&R:sP>%#Wq72՛l[\Sf_I2޸xg0gl暽r*)T T ]lTwFLgK=*'s~)f|׬FI1?o| UL.]$"r2ݬ2tu'0 hoy\PS,W\m ҹҭA-p(Q/rF ]W|lwz|p?,D ->tDznb^LD@&N8SfYzaބeF6U7+zE?jjNƗo@bFzFxwWe v^y:KTkhYCי@[NhNYżtwU͕rB\z]=~TH 81q)/*6.erb?8Ľ`gigF5V AsQ ̱NoЬev_~]VV}hsl_l[xlT] #BGr\fkAh!!a6I5\N 塟<:,R]BhM(&áˤF]'FqO(Y"tɆh֩5&#(L>6T&6 h"(|nP Evo8b!bl&[^M >y0פ0AFU*Yhsj'ԛFdt:lf2Vg4]H_dfo#(qٟLE%F6l`4Z.?Fy1lZtiqB@ >g<@聿 G/rx1Nn^m32vW+_Xݩ{wkM!L XM>㴡qm۵CH|t](B,O f4:MV''>  U@_M~=])0rk3\6RnrO3Gwt8@"Jr5IX,~Exj{ 53MN7ۜL,q{و# XZ͕ZXf]@(n? kش+rD!ιp,ej% x}!Ջ<~Ju](}EBnˬա3\?Œ3XС)]NPxpy\Ai%]gf=HbѐPr[u#`ٗ"‘-\b1K$ْ u綀~+C~_oӹ !~B^썞jbpiZ/C ˫o;MDo٬&IpH?ly'Znp>ԕ*N.L&N+)z=J3EWF>&DO~`w(%xtJ]N+I'JסLWJslC\baJDD4|;D띎h*_,G ƒAo:D4ćP&zDh-bsǶQ&rT|md>P_ZCʟCtrtDDjBvǏ.G؜)V+A5tҭ1M YaD{PaH(y7.74}th"J3( 4DD"6N@(I7T:7,Q"s)B!ePV-<:^oBI&M:n(HD?y Z,L PBR܍ e}Ϝ}'z*-X|g { yo?(?я>0 F4utQjqc]Wh[T9_G]hbǘbq8v&L^&Lf2IL%Tbfjd 1cv[[?W {-g<&0Y.'Qi?.dlhnqXe3YR>]$"Q?t6W@bl$bbbƢH~&+P^/}bkw r0VsEJD<>5w*33*A$J=YkK7Oqm:A9<, !.Ce߉,Q5/☝Z!zva.+w,,2DIZ\T8?DhW9  ߡid>,V+dn"C/`3mF 0\c'ߊ ћ*'B}dV7bP.T HpقodbJ-K"C$s%*O(u:/%)dѢS+pPDHxҪ$RZ$W8jJiloo nh \gW?Q}w!]=~kQ ru;77tMӛ2 ƛݗ7e}:z5eO'l?@?{s^aJxړ<ǁݜkX߇<9 /#D{~&w7XA“_!«&Dwc/k6Vo*P(K`Xo vbJo0,J%Nop~|k3"BS)5Bo%2ըɴT G'I!JTmHu_"~Dk:_&jd0V}0W* /\vJYzJc5 Z7*\&[m{Bvbk{HjEPkjZ,VjtۥZCx6\vfȌ|Lz?:=.;d2͢&UZZVjstp>DN?+FNo4r|dԹbN:#tTYhT"`Zl6\,VjARF\o7Z^c1S1*Nh0CPh^4{n:*5# IX h쵓novӹPXRUZ%1?h.NAP5t[NXil6/rwx8WzdYP)nCTkzүwGC4}h=84kT Qg={:xp>:>NuS5mvQo^/դ_|rQ{G=tfTi*B[ R 5t]<]\F'רJQ)bLc4P^z~NzӢ7یF 0HsO/\?zpq1윞s+Pdihj&qݿuw_|xUh@daΛGtggBTW,FX~?/=>~x዗.L\Q"V(FG'g/ty?x {|;egtzE\Lw_DFw O~N{ް}6:>:~3cǟZ{߽~vrr[ՏϽ`rCD Q?/;CGFˋ^|ރ/?|A_~tztv~ /#D0i{ahRHBց~≄}4&' 8 k&Kdb']ݗ XaˤӘ,=*@:E(EBkk'ԍ}1pD2F|טcꪴ8d3 :E2pPe݂02LCt N*18SV ^5/xH$VHx`0L8o+vڮ7 xMVEg0>mlF8V(ƠD~2%p ×+bh4j,QjdTN]gY[ mWR[ ZbYl.kwcI`p66u\PX:z6J7 dPi Bjb7J:e85>5@Z\X^\\tZA fǧHM$/N,..7j9xJRE-SSwLSVI9"á31m4;99LY&ye]iumuLQVI$:7ʧﰸγL)ĵy,<}4;,8"\,bs\1\^=Vdq8M"Yeo*|g!DxkKi2u~brHZ]!g BFM |y qL"~wL8jT(s s3ȋK[wSa@QWǧ槟;䙩yXũL.a $`$X׹i4F& B>3rvO%*B#~yn~O?;1>E^]\2\%@a8=7=4O2/M=[wgv]X(cl#B1(N]_!VcJj9ssme}27C"/,mcPɔ5?Й Hb0&H2@]B;V h>pߡCXחi"qie2ᵎYt:b1[dw(te6ّ1DJ&]ז4a`u,6L)Dvr2 E劆ܑC[sS==8~kfuڽd[H4qaru2O&,.\CO$`x.bp$?BNcpQS>Y=V틄!_8vv4ot,OC/,+2ÐHsx 'p؜7 (܀^|+W ݀ݔ}M [7u;n 0ހ˂>!7@v.oJ5|gWGU8d}W ҄_mOY>wKW%V޿zo^y^z^_5C>D uF癃/]]!roQ Gv\Tzd =v] ZZ\o%|?׉1F N/0 ?G,T[B{W;^{Fcxqkc o"|"~ó~-4Ǝ2VcpZK9d,R~hkZy6r?-e2v_eN~ZT㔪N'TIwD\Tw>‘B>=>}J4TLghKf?;Ox;.57y:jByPtˋQ NR7a~Aި]b.WPΏ?jW(CH+4[C; Qmz9߮RV';>ցnGϟU Q=l&S^V+^3]][ǧã,Ԩ:j7GrV*5f)WpǃjjgÓA~4lԎhE,AδQVj4b{08>(1L&V aQi tð/AHWVnuKN=\?vGU6(-~;LU:^|kiZoƠ_ MXr6WDyrqѯd!@L"5_~v>@IѨU-՚jZ5lZ#|_:{ .:GJuzS/ /_û??*"Q` 0z<_ Cу{g'l95ZAPuWQ / /Σ^,{Ѱ 6L6v'?^\:G/><_}UCC>xpT<|lpϽ◞ /M >wq3S?O/?|HA?5ڃ݋ s4?|cx4{q2:x!4Gǧ.Oώzw="w byrŃ/>BG~=Ͽ`8|s{x~zqѿݓP^ “z~݋w//>$'w\lN9PT.׉^7'w nO<3vG(Fu5bO(YdQӥ|S(f R}YV8; z\x>{Ȣf l:{BP8CT2dQ?C8\nR% ŋX8WT} UJ07<y]䟣>ǁ?$pD,^vpi$\sIYkvhrp=na &ދvZr9 eyh1p2>T3T2 EMBJ4=^846Eכ^]@ "Iżh6q[E=3dZΥA2^,]Kd‹ȢKVVzv>W$_2겸CU\5[lkG\ZKŤhsAy:\fR.OO:ZRȧ3#0Ax}@y |YJ,PܒVom {ROd S7R&j>7`AT =~Ҭ*|,+X[Qթ xFB.:2(Gf-EhZUI;Nu>l.[tk@D>RPoՋB5˧2b7^lbiuFz [0VOh OIT {ҩbR+UjU%yB|.jtj>8LV R,k՟?B&V Cx SlkKdR뜔Zʥ8PT/C_5}`Q'fjr**R0O+u{VRV9>dQ tC.[,j֫(_9.vU_)H"kf9ʴ{6iFۨԺG'*S{Gzn]N&rbO~ 3p@Tiul&D6B n j9ـ jvAv`MA o7U"gJV.9BD'RAŗUJH"sTZ)ԸX*VJ0hӘͦA}8aKy\% VdPjR&x|12H!S<.dP7!pT)ڥ pLfFU xbL()1,̥[{V04K|.PKP#PX2| Ltsec/1@2Zb qޤi|Q#\:̞Ybˁq.[e4rg6⍅M"FR%er #Mrc~h 2_%R}a E|\:i8a@6.S.m3y\&qy:8uXuH֋l_$Ȧ?6g AE]8t& lj*`<`Ch29{=f.{fܡYdRjE'^a8| o^=?!"WH.RC?vo\=~>zyG #W= ;3HAO׹B+W&_z훶7~~U=\a:Z3:E#ԪhteX+z0-*P$Fl3z'݌v՚>g6zjx0W؜ZA(fE\- A U[,&L$V]UZ &*rޤۭf#V2%R(a 1٨W*40BkxF-Nz j52Qzijj7*4V]@vYi :Z:5[,vZ:"d0thB hժjx" L JXi0%RB :N# %rd6hULc3&BJ/!,E1ZhTI-ȅh`2Xm:l7X4R 5Bj 2 )VE:RĕF uvDQJ" kTaB7tk<(.4LV"SiuRj.-fN|Lc#j 0 FVZ6Ә̮덖2NdשRH/•Az'dWhT2`Ph ;8d]ta%Z|v͢ӻLr2p^=?Vm LiL Rc e*EVj[rAwl: [#LQm2k0f4`pSʁLJ-f aթ :eөrf}|[o9fhaZ̦1ta%A͊$^hedT5UYG^r.*EM-Sv ^tz^nO;b-Rpꥸ¬7ԫF- ZNa FZ.caj5el`sowsElkcm"1E!xgow{!Ĺ<>XH锬]m!'l0!ɒjb| <G5}Ze"_rl&X[lbF tRfB  ٴ͝hM 1w7wXJZHXۻ1 ݣh2d aȫtTU*Eb9cB("}L 8lBPÓ+ Gۜϋv6Y\.&`A,*rJJGqE"m:#Hy(k& (œ*"6Dko-.O-1z8/h@tL 4mq#DLHQ$ X.8eXb.2X$Kdrr"Dܡo386#q"@(">m-co){w/<sk}O@[LJY1W;l.P'Z2hm@Zp[4M_d .1wvy<cw%a8D:WYh6@mV>-84pw0s B! Xdricr8L>0Tۼי~Y֟%bG2\}1e#3q#4D DW`* "\> j{ fPwk{\!.1|6q4Xc:1w0˷P K)I &J@(?dF{$ $6&S_Dn1a|>{(R>H=itkw'Qw ZL\c.@vA:KoaѸ >APxѶQ!ۯ6F_Y5>ܭI h{zbJ]Z"MA}ofmg:q[JڦO/PHEuscssKsDQ6W榉wfK$"4=94XY[QQ*u*i2yiamD""F&.P KKs3Y\Y[^&/,SD BYX ݳ0 sX"S7KY*JY<;B,/"ԕE*B#R73w&n6gH[Oߡ(WVuyn<3$?95 Kwn?3E$Qg)ɱo7V;ęɧaצo.I()HWmlRKd2e˽B$ޞ ./g)㕵e2܌RfgWE4<7O) ŝ5*uskcqrB!/-OLR6@[OOP($|~%\]X ޠL'M ?#L<;5?7?gcoЈs uyq)yym%̓H('uyf3e.}gbj]67z;Wo8Տ=_^yH~rޫ^y_?#|Y'WF[ 7; x_B&o m /dRѧ/1 xCQQŏR!W(תL'C%5!tc)L: O4+%R"JMQ+" `7~Ȑ$V,A/MZ tFT֨%*b @mm3 cؘҨXQ]n kA1Zk`JFJ `4zE#VKxH(8nɥRji fB>f\mqmG)Ó*_䨰 U:.SNF,l6QVj6Rn00 rZt tSkj ^N! FƬr5:d]vشF΢4?D\S qS+@j"(JVB}`P 6*U)V+S$[-bƤuflC@+xTeە(F^)r9Btؠ9 n5TjeV,@Xa*uf\) ̬j0f蚬*̄(bjx]#zjwZtx;zI#QZ /FH0I:kI-HA*dG+ivǁ,5X:pkBZoq聇`UI$rҚ `zb,Q--NLgaJT4Z rH4NVkm&L V&')6YtJn2Q<+kjfR*7A t҉E.T) #pN1d:^ա*'zY2ZCˇ/g*Bc5|A֍ T.E-6j%RR3z3FLA|czG5T~zdNc0=&Ziz٨ôzhi"Áɟ\]Uw{5\3L`x8WbtZFSJDVxF7(٣n S$ڏFrh \ȸj#G76#({5<{*f{0hԫA]:ÎZKWoBq_&ҹj-5Cjt<8;R{Ъu''J+}O%Sj_Ljuc:;YJƠVFsJkE997$sl8կ識 nOGF# g֠i.ggFϤj鐱NpB.9S[uԫ1_eMvzJ<6մzyYw@ZFBc?5ўLL=]l6RhFP67g;=;BZݻ׬5h}(&L0_vh?tr9߮d‘R*~3[T<7j~S\y=8j;FSNjN;4r;Z1ldKfҟ Swput{gZjtU3ޠLNdzڨ7'ggGd_JN.j}t~v|ZTASJ$:ۨ Fa9UN{'KJ<<͗*nwP-6 džB^ֻJ;mU^?U+Z>Qʃf3tvyQ54RݨVZ''8Jkѽm4zY*gciZ7@6F~ZJvӋQltaA=jWzGǃ٠S˃٣XC@puB*<VZ .tGFwpr=>:>?b蜎Dxt:#`bCwdwF\oTjV^)v85* Vn_pZkF*YFRQbs4\6jkh8jJQZht{'VںR,vPYR]A{r׊z78kۧn5_;wZppܭ^ 4}P%A A1`B.|lRWw|\,dbl {^0x<WT`lOȡE̝]䃊'b2w,6]L@&B6?"H"blnJ)lAg%GG/!_ex?Tr1a_ 0\TK}˛L>O1mk_$W2%'a+[ljEB\KgӘLD*foa|&jP$x]6OrP8]$WYB6px\>O60H6зitX"9l6Oȥ01:[ ;tejB$.kme_8tF(6Yg6_h{l ۡsq%rTq9@v:8-B嘘,+ X\%CT b}͕AP,Y,&/1 <^[eq pQX32+P2#J 0\X`&<'毯A(J^%Gߤ4EJ.z UL36YTv|㲸{4>-l3 |{+bxX* SB9+q|1&Q=-^J29lK ">qLT`Ie@&VRO㉁L.wql+$8sG(TqBVęBÓeLCf% >9|d \`L {f8 L&vh}: O$L6OdD9-H.h &D߆!`@W6) ê0w);ZO:lT* !ÿK{"F׷8|`"Qʅ=)ϗ3QY1h(2Me1Yc,SoE1<0b1c{Z**dbd4&Ѱa' v77]Z hLLʣq靾ScLDaCy}>ŝQ^E\^av|P@M{JD6KN?9"\Ɇit>Ŧ %˕ۂ ԊÀ'l^'JxT>b79=v.pgex/U>0uNb.[(4=Tv[6:s x!ض:|6DآPejف'=Ba0jۃP8vuዧәb.qfp\ %'#FۨVx. x]p,A7ٌP4B! !?XBIu"B5⩦.IxcT65kM^pio8;d%g7{S/4AW,CJ>W('٬qQtwy/@8N\pᴇ3GmڃtUךtʥ6xD"jdh2,TK!' [mBa U(ӉlŸH-R-qP)X6 PĦPY}t,2`n{j-(%wZGT" r<bdB&sZAh6aJ6ϧѸ x}ŠjxqG uĤX̗L~ QZT"9Ff4H%ÃTD2T&:l%hs 2Kijh%N*h n`(Zr:Dӥr>=D&rb6I~O8 fWتl\H|.w&Oِ.[iBA`x&E+T T}%/x,`ҙD,RI H2*D>(*Kr T(ͧ\< EY;^,s|1qW[ fH{cx #F9gX*!t}n!DאBg[w-QKK >t|2uqEs+s ;cߝ_Z[YXL,G'yauqf~j|27<7K$,/u]Ajb,̌O<7A$Ng$$q};euy4O]\\!V3d['(e*y6qqmse}senr4?0$BB3M O'.QgnM.-)EDzNEԧ]enL/M?==$sɥ<=I^YY\XZbuSQKS,c2m2eIן<2OO´ˋ۫K$XTJxzjn4LЄש@:ejBR&?;IZ\ == ]ffI qfa}i~+Źw(ޚ[ ݙ.R)䙱9E@;;''I*~P$4;DZ'SSd;9O>xzfiaesgyzf|jjqva<3t%>9>EX=8?9AZ.nR(D"yuf C$R֩ksS1rtS3ӳyXŅ[յ%ܝ%2qfb,,/o,!aZY^\)ԥgW$$&yjfH^_[RoOܙ R$xJLݘ'NMNSe H&SITL!ݹM\̭ɉ9g: }ZX"W)ԙI"u45L]'QMR66əɅ 44?̧>K3>K̓זgAHg /Yi*c d}P`nss2AH ӓ ˤ`ܩo/.A~&fWV@'f@́D-SsX]l2WWyn٧oS)ť4MCĉ˳'=gIK[4..rJZ!Q7)(_4J֙yq4/Oϐ3㳷);UДZq3<8 N|fVH'BoKi"ONl1\.bVyyzb84di"yqy(9=FgR1WaKp>C_ȉ}D]Y]$͂Z]=FWE 1?OH*+Ӹ Hĉ;D"v4=2g; 4׶$s9<| =B6M&fp18ӤO=KR7HsO?K\a m:GTR6=džPD'Idq84u}AA*5oE$R\ӷ콥MI-411 @,Bd"r9[t%O@ aSÑw :sG"f&W&2 ٞP%l,!Ds.'\ŃH$!0"ۤL aLZ_Ivx4HN&Dmq,-O*LTl3< xh + R%'b;o6tg)lL$6x-,ab{L-1Evr-tŔbWPR%L(0SL>'M*D; _avQmU2K, EbJNo7hB`_$B0L Fœȹ,E.JKq)T2>mmשaT67Hp }i}Yr]T|YeKUJFt*e6N!XX4!~ep5K 5OJ82.&RBF"LTӑ`,Lx2LnV,l\ ѺTP1L\\|RTJhTJ+Cslk1YL,JEԛt*J%8&SpKW^ٯ_}=W^߼z+ՏS|Wo"PrWO^yy'WM|9Jx*>6jk'Fk2R@«2ZŨ"(W`bTJ:r_a2,Fa3uNTT-i TTeH9h UyA3Oa_?Ur4 ?5jJՇ(vo86|iFum.%Q7?~ Ax~Un {9TFZu`pE fnJJ})' Tg\@wx c4QRi+_hBJhgJց+kwܯiBb^=1\n+.GB"y 8Q2ݻEŠ\+ǝf ]5'fWݻ(w;~1Lq=9Y/׺PF>gNb (T}0s 9Ƅ viL.!C#d%vylFA&%vV^Wݤ ]ė{>G(`C0DG!]_,H dtЇزO!wwĖ.A84[Xj,7'_vA{ =@|RQ?&sQoE/CP ;ґCn_:}X06X]&BO2tg GЕ@,eL}"sjyBpl xX eP !4e}sx+2I+WJOD0|/SGsU6~C<^"t,q;iHr85ڢX!} bHPD jU;mnTd;D*g>/tk OGY(KxB hPq)Ka1xR,FLɅ~Dۻ|QDZc8:mm0"Zۓ()S1bʫDLy%yƫ|o iΫ7rW|zy̧)M{sI=>Ǯ?}vhYczO~y4_|kYpϛ݀ O |5x Xy WOl_ǭ&]=־V `#7nWnk\oz@HVUz,CB~_:AkUz:]ZZrC/&l@WRWr[M3գ^&pkL;T%hd,^6 YɨUf1JLVFq~0Lgxܿ~ݬ>W =5R!&T;frMQ4h*~TOSfJίaڭN[U*[4~ 8-@Akr8n,|xXMz6F𐹵xG8zZ.Qjfä䲙ӁN %EE142mS&UVzR|tP 60TUZb5 F֬!R2 zFk,va2eQ*\j%2#f UlEVkG-$UQ TdZf ^PtfZޠTi v H:TrZZ#_@I/yZATk45ėhYo0Niթ *TmP`TxRT,EĔjAV0j Ls} ͗$btX4p6*gX(SjzP tQRijPm418_yU@+S+`F-dVW"Y-5jT׫ ro#ʤ*c:ޠeW-.xX5ZurfPP"w[?0U;ZS6vګZ覗2Lji0as+X P{aTJV qb>mr(.8`ەldYi0̟kVPZgiuFA!QhL6t`Y/SG^5[lVha:rFe2dJ4sѾo:5Jt2lTcZQ˜uoVDn 2ݠھYf^!Ȕ2LÅJRS5Qh-S) JTIեJSl2r٢d ^h_ &:Ո ɤZ%o V\kWKZXPJ~5`T* ZFoQA5t脪H*Ah .4&a 4r804|*j46lVi$"j3u2V34 Σ1JE8@mvD)vURךRF!Ճbk6Rf1$ mc X %Vm0hv J4dth1d Me^)փCXVT$ <.Ԅ޲ʕrd :!_f^Ѩͨ Vo6 Z[P`v VV+R17 _w;%5*T kD-46 oҫ:^6HŨb9\R B <-?CﴃDHzOƤRdǕl=ߐzN l%ѡ 5#%Z@3H*jxE$j`* rշWKtf-:@kRZ RS)Uh 1.F׵j) *Z6W J@RCh@0.E>o} 4BE'n EăVA x t6t6ẘ.p kL'oi-a:ewX;L英nn y}fkk]h\!}cc})1Ao:mȄJL+{9lW8?IQ&pxR%txr>GKm@2׮ʗ~dL%tY;GcѴjq"Fs;;\>$OЁ`_BeBmvw%X4fe9Lr4֞gkMsBTM34 ֚ d XlfkT,Uҕ>Q;?BJġos\hRso b/"SC(&$jD=}8kSU(tIPU)3[ JYi|%>" @ 2ma8ٸR0%.Ťb&yX&qf\ACu`l' |YR1\q6K$;gw;^-:6.l~mQedh{oc'q]pDT6\&mз|6(9mԪq*eb1,&O(zP+&:,. SЊ.Ԟ+gooS @ygpy;cA}Dѭ(џYf0@)G4u-wL*DAwd}] ,6P`lo,bBɂg6 \Qh; =mc8|W{LK  @dqxspet=Ă/^C`0D"L:'Xt&WY|˕@wi]:WFf=> ;ЏIť#"t.`,0cx9QDI2` 62r)&. a21 -co ٨/{,} 3}.]&gi}>zÅ"uX=K"0dKYBWH_ jt쎏`0.}abj`1i P{յmX _"G k+[ \*aĔ+l>}wGvY*`. o묽-О<*sX465:TeDB 뉩9d<97C!m{FqDgWf E4ӷL-sV(E)3% 4D!M}LCY*\ɳw)+ SЦ*u]?naz|bB{2~kB3>Ң9ikH_%R'5N$+T8}dC3Ĺ͕e 2Fl8W(g}v",-Lznz6#КTjSA/pVyH\&-R'ߢO>bVz%2qfzB]]ܚss4G1+$Q斨d"ymwJY] RƟy6iF^^[ Sd&.,dc0gIKSss䥹92eLLx5«3|YthQ@]=[[pfn4O&M *8I&OI k-WMam4tH!oM,L!OLT H4iJ]YYews9L&P&YqiiJ!n䌹viuso2=A!Yeyc>>A],o.O.,o-"ť񉹩g[vVgd{$myfi֧'WחQ 2,u}mӟ&-.L^sQ_Mͮn,..SgI}$ 8ZϮPY=w7Qfoon.͒@ئ槷$]0ԧ\[XA8579":M>w@!O1y,Ժ6gf@ID4Kcmsyae:7K&-. =hK,23O] iEH((A-CD&'nh"J~fbUAۤ8a~ͯzZ П=AD /Lܹu{jz,"< x~SIl[ <'wfѦeW&fV02;Ng\"%pMLܦX,y`E ϢhPm.Wgff63O^`e@͏OO!Rב&}C\R(3t}9]]'5-+Y3J wƑ\YOߚ^ggWV0s855G!)ۓ䅹 hy|T$;hHF)+T O?usyv8ڳF!|=饍e>j'=07 d!a2ěu4{{x8&o#mEAވ@% j'Nmng3,fgѮItt"Af/#P=92B|W“^ ~~<~Շ>LxXn7ץ|h FxWD xH77|gW@(o݀_'߸%7` xuU'/^ '?z y c'r^7`XAhon{+I=ջ(߀o~  / 0 7~+WB yVlt.5ggJ7qz^-Zߪԛ\8Z=Qa- nzѠl vo0ǭjlBY5wGóAz~>JmRԽ6۵Z3A)m6:q%B>jfPwvhЮWnT'ATj4^ѪBcSɷJQm [oGAR:r:)~׏[rgxzl4[Gjoxh@J6.wTnz^up~q>(. Pf,mwjZV/]lZV:w a-κ OI#[nr5JvbԬ·H.a2a,PnfD9ng"Uݬ[_wVz͗/OGZhjVs9,A\f+ژSS'HGJ%گ z>bب2 VjRѰe ZI|Ex=_/bD+^VON+ cu^#3:?oF{0 <7Z5ްM$T"Y2ɽѨ?hw o#tZn3Y„+A\/?7ExecveR۪!&k ~-oJx7su'0vutV+TY4:FÛwOa8EķBoU ETu o yf P F7ۃv9%GuŏR}>읞j֠cu`|B$VnQI O\=:m(WcJ'~/E]JM/aYՏ~?KKq{Z_ԋBq}~ @"\w{1?_$<|<^189ePl,UKfzUYGDl+ZtX]cm4gycQ7ՇuSXl:RX4OOf_吒8RI@ހӌ͟7۽4i:XvRTcߞo\ns8FYU렺id!1 >띎~.\޻ԯӓ Y' Z\&Swnїбaamu:R~nR IA~FtB(5༕ ~e~_KMtiBw~z1ʁ&J h<(JݓFQ/%[j:_hӽFw~yq~ntNNNǽr{X#ixy;;lK?|sNONAAQuZ(IoJpt:8<{~c0oM=6jbT99ZkF* ,}F>{/K$'[O 4n?2f4g \>;?9NI'ǝB2֢?E :|lճH먼3 &XRucTL*ҿ8|ul{z~T[ˤ̟ $bz:I:ퟃ;DžA۩+V.Ի;Sب>n'RB t[veB8.yWIe{{_A74VֳR5t,v\*/u AR>;Cuo'|]Ǖ|SiAԲh ?"|EZUOj?曟̦dWͥ*rܟg7{ > Fv@S\NA [ݻOOd3tlTrEOMsB=8? ? fժd lRkބQ%^7r{f;[Jvf3%H/Y +õSp{j@?4jv:Uu qP xj_iMKb\%Sh0V?Sh?|3_HJc/|H$ra]i+'7txTr})GH":Z< ` tgP2χ^o0sBHN;vP*&CP@bdY.<8h2qG4Kte:ް+` %"X{C=˹b6~FK.cAn"C7'?L <!]p[,f?+eH$<̃MD"3v+i7[.&#C1XLc1ɺG{= ,|h BP{mf?B04d+lԲvJ&vF]\)/$.ײ~}E \"/Ia"̺ @Ȁ'rX,0 }!MbX.6c!̦]f{[%ڧ]|͙D(gs| H<$Vj U  c/@oD =}p>gQׯw:JZ@|ש0&c:fsan.oAq̻n˼`6 !d,C- ?|?SH._8OLX(ESِϝ@KZBB`4!֊0 B{ʑM`.p_K'M'ISbO+Fw?.Ϙ47p>M9~͓LGpPâЁ; ]0H.rx:z#cTC[(D$1jADH!TH(p0[Ȥ:e=I;xٸqx2M'!NӡH<\kx)KMq=(EL)px*kRc@8tpcgH|H L + z gsq?OVmR NQ_ 'L$C@b2*UЇP,ӹ9p:ܑBN\6w ;п@pcb"U g}Rgvlt!PT<{N$fR!Yd0KD#N{M E' t&.,ll2Q\p:!P*i#86? ?#1`Y~jp2Jk(ֈMAPg"M7?,>o~&h\+~Zajl10`xC?Gbr;>v\`>~V T&'*継mN  և>d.bS,_NgP00\hQ0lV]T6ϱ(=G4eŤߨzbw0x4 tlMu%%bJXd(:a:,  D<`w%u0rd&WktH'"I= u+Dzx?6r:v9GP89PxpK9!edr=c# XmR(y/7`1/_ױSXL&`SCQ7]Le'e>D! 脹*׹Bb9 .cY_X潆8ph@kl1:Ϲ2(7r۰P̥Q51׫'}36 JaB6'GFUR!6ybCW dTk,K$1Ŗv\DbZը29Jg+{J0euZ'q> 8ԖR)3XBPQeb_ec6_(pm%uݍ#,- eĉŐ8c b Cs1sUwuUWw133bfjqbL-hww{o[o=iS{>saR>n%ggl "ҪaclBI(\&mL=s:wA59b,/t2C屹l&z%W0YFepr*c rk208L3 ;Dp.$,*Lpq<',P nuEcӖ(lL?LiqF,1lDgҸ,[Шt:`r1C,rRS `P.Re4r\L%&)W :ǚ0huM%dW\ɺdlhd* $Ҩ/qADUS,(VVlR(VHϳ 9O,˄e:3YlM)'JUEP*dKOS/,\2ኘ&K9TfzO4BcYB(/8,yZbȹK4Ę,Ya-,tb HeK*eȕL|mUrH1=9!n 2]ER)~@Dpy#F2d ZF%6K^`r"D!?HfIx`.;;?3%@)ϟcPw$mPB1:OPqH*HeዔO]7ꟾ`$s!\_[Qh tJ a0B{ش2?:/;dT2ˑ.^E"PP Ǘ9"Uߝ]P)0}&BH )C뿹 0ϹblmK%DF[bs0_"R1_jeQg/=}vv nUX&0yt0̓@/|.ĒH%4!jMZ _"-X 1N?~>ä/Q@ѱStREl:]$*C/,POa ph >-ҘٺJ&s4Xr iZr\[O-eaEsU$ u"#_E{":HG!)s  XzźR/$ҡ[<3f=p5ڪLJE"g%}D:G 8Ҹ21@_<O"Ο;s~&giưMNxnf>w3%wSa9i <#}-^0KJ` dO+[_QW%%ً,\>x.g"?c1ɧ.^\^d%ވdx9W(ex=n08կn _Zf"usc]+j\z,] +'N/s߄ dRҙK\ O2ލa7Y疘 A5HEcsA!%//Pu H!Q9s_1a9|%oøQhM-1"@o ?O*Eux٠) ٦\e"nߙ "1(o03"_eXg1(>XfIr[&p D[<`|Obɐ>qN>܊Φ!vs\|E)-_PN _CYX8R! Ds▥CӌWҦ~?1 +\^mRiԓJ>՛Z9)JUr|ܬKD8$K*/sU?׋BJ DJү\.k[\+NI ]՝_VdZPR& :D&ԡZs fMzlD*ø?>r BOl/Bn !-U+uoz!wn}9B!ާ(BիrDD9V{fK:{#~W sfߓfsBrw-ڬlԤ{' ']+? ǣ^Az,8 z^.}U9jfq5Z7 -7+W7h4ǂ 7BFW4 nd |rj݁jvb;Hy/[N }^N7"7,X6iZLqwn,N_  v\em~@$ =ˬTlX^"x<W`~Voqw1jӫԎ@#Ľa a`VpD]Vی w&"œDt eĖHZ]PX\*X哈-/.4.Wfx:u Kr\aBPf_g^ t-pDTrhW~)~ "}Un*x| S[SN.mywqYORVRDEDߞ>q*S&OvDm$y| OF 2G;,zC}"THtr"nɕ 怷:{q7}Ǥ8XnDܭ'l*>rD=u Y@P}\q3" \8K!> fQٗxw ^+îuWwpwF}p ?ގ{)9poZkU)G"!U(Tj{!YJeގ{͏t y֊}p'WȴĂ*dSK_`-,ys;)k\"d_{gΛ>/{IH~?.sbYR _ ~Xo Sd [$py 6:C}AYO!oXʃ<=G.~drYS'/P,[b٦CPΠ; {t2'_{ky78V>yD#Y^_}9G#5&MKoAz`ѯ/~tu[hG~^?=xߎ∌u wN|)wNn& GtCz;WG<5#_Gk o^׿ #3@^*MGT};~/{: 7|Zs| |}eߢmz[ɳ2}|t&+c7i&Q|զHd 9o1% QnhVrSQU`].5 1?#e ʪ "bH@5Pkj͑E~ Y ~]u@"CxTk+b(=XQ(^gʚu)m*@) onlʔ2T& u 郤mbŪ3(Wdb 9tDVRLB&OPV,6iJ`Q\\_Eu҇HWkUUFV ,TohZ/RUr)*J&YY R[$J 4+J\r*xϡrW7VhceRtM"&xYQH5FE׌5ERiIJ^R6'I/y0M#IW6z'鳤+n& }t 1݁s1wz-r8T_@_%0B>sΠ3">u%[,cggϝ>{~I¡"v(!Μ>1"/0,<<Mm,Pf 03qG" Ễ|gǢ,Ш1;J :Ü=sIgBE|6 ea9<`e>YR斖9" 3?Å 4K2YLy⹳_*eqaqq -#` sK;1 _<1ah,>NP|lrX& $2 k" a2Fe 42A]8yje E<%&e<2oh42"]hs"mixA O F['y|$lcD? 6+QPlN] õ :O(\EQ)$7#?=ࣧ?ȉ_Ql<'=O>pyG{;q;wCc7xSg|Go}_љG_x}Տ?>rϟ8#~LO̜ӣ?x}Kϟzs}ýxӏ{¿|<(&?z{CO>{XO>}?z㧰 <~%'N^8spG=cN<y'^ gQrQr9y=sea ߒs}gO_509"##r~?}%,[|tHG#W@>L:xqzWۑD/^> kAC_Ŀ>S;Igf9+@3|ڙ`+?ߙ- 85ShKTDV,}(F=/(4Lf֕+<͓,*9O!3JR`0Ci|?Nx2$҅E :ɜH5>+R gr~+P.RQ=\?;"HI/{'[ә bDi?px| ?|;q^RP=<Ï?ȣ(xR'N13ه@>>@>2} s6?4z|-  +IL_|W=Wr3;W_$ioӗHo^MjL^<~ӫjû/>Wvzgobz? v=U0upe+؝`7_/#izM`3ӗ8u{u>}o{࿙{>{ϝ{ Koύ$|$ҭ+쿿_|~z17sC/ʾNy|y>i+6}ןz>ཞvflҌd>.H3ڏfÇ>PI\1^e`ϓOe$& iur[=IF ۤϑ^4%,&4Q撇&l!xYDopBxras+vi=ln[?g9^Y,l$wBs">8l1*Cc6Flޣ.Bs_vn}`C¿~H [~<ǦB?v>j@D3LIz&' zZ-W.WWrOz6IFk0Z } E-ҝN?_+f+nl ;\,f [N3A#u.JYjV6QZ?ru Xb*mn$>?n`eJz'y>~cܬWRܪr֭\>yYoTЃP*˩Ñi\-w5_<ѬUᯃE6_6*NWJAV)`,⦴ l6?/F0O lj˰_{q՗Y?LIJިܔbbmvݨ6 o;p+}|[wfKeZ^Mƭza|fv*3 :񪀨%۬rJؚ["D ݳ3Ǿ |qI"a:kv& bV_]x?J_Z NVoVX-&Cj\.tjvłY_j%nFZׇyz)Vƃ|kBB[fF>_rx(ܶX˝vLL*sDn{_4LI[jTRUʹTZ7;yB3veZ!]ۭKz,zy`o6|Ac|"hJ&rjo<Ġ3y >ނVbNX .ՋiJVwЯd |:_"bQ-/o%vN@Tl[zeNB!b hN{Ɇ7|uJ%,Th5]lWJFlRRm1Bm (yMP(f3O0FC\\KƉb!x#hyk3rREx,P,AqfJIbר[\]ZݏW󇙹~1NW*l qhx4% IGڴz6^8p0T(veX5dvFBX2LFѐ+B㍇|nH$Ph/-MdvW[p7;5h8  y1و; 9J'SI6^[!W˦]׺ѯr͗`GbѨϤwD^p,˷vktMh0P8VtFX\hԡ :`NIW80ݾ@ ElL/Gw*Ӎ;Cxn2p8ϤA Ɯ%. AjwA"?bj-FfMN+6l~ڝh> )u&#*àjB6:VfG.^|\ֻI$+ 7wuPa GHԨ2z?rd/ YVD'p&wznbN^Cnܴ::}$;hNspy h/iQ4r赞pmXWH!w6a-h/n}>LJţ~/wh:Nm>vh/.Ne #6ՆuXNa95[9wMgOHG&6op8# OV%U5=qڋul6Ye :ݮ`$'R>'^*TY/C{qLw:i$A1oрID^T ߅3vXK" Xok|D6ם 759Qu Ɔ{ݱ/q\acҙ@onrlրXes2@DH<Ͼ ߃I !ךRfT,`m:M}@"8kOp o$p،:?ERQMk^,wo)`#M/8Ģ!l;=qB>@^љj5h.n3Eh$w( {|Di`㋦X6YDsqpkFR,䋦|GZQ9ChğUkD*\F]*1:O7fb o":tl!܃|1Zu-[Z#H L2 $JՈ7Db?>r7̀g E Z <J$SHZC'YTH&HDDkIB+XSm݂U\\Se2>Aܢ~ki~s@"De+Bh-4:W(sJSA>mvmN bQh|!y@k)ɞ~-`3L.^XR4&k͇{2@cAg%2!*rq9_^RɛXp,!`xbhyePދT*|q̓"9F?~%|T_ OxUvǖR2/Ჹ,*CEN|RCM.x.$%m~2yIq=՛0A5%-D"O]dBgރœ^fT26REKdrŪD"jH'ă.WfbM0&C*3xrGeswד)9_x ChBBf W}P%2ܙgxKدėJJM*a0Y3]X}F4T@(IVTbC(@8d&6{r̂EHYrdp&V*HN? \8 X4 Op@ًϢ;Tǣ.qgJVT.ҩ<\H2y@3񖥳 L*CFJB>[.hٺX ᱅DgP<@%~@DϞ\ 6 ZD[4*W0J" W߉\!JXi-XXM1;Jy+4H{0JPrO[WŬT&M"=$RWJbΗ K(ɵP++|2MD>u"K, oC*T&p 0T߬%-Gf7jV`m_5B%LUr,fqiOx{aVlG:$If\(VY$J;ۍFkg?)*In R'"b0F$Rwa|aXIƢhƲiFB1D(6~_0E=lzZa r.l~+"̆i yx9!bnE lCFGkT4E]:w4H7`DY&we1ɐǛ l.Υ`v]H^_$"d^! ?~37j 2Z0]D6  gיl5.EȦ7rZ: xmx5jѨJUGbt*1qY|ɻ + z ?pXuj׽*6kZHg B*F]/NDS6۝^St^2,[B>=%GxIyoAJXN-,I^Bp@Da" ‹bl S c_8I恇Ϟ;‹#b ɠ3xd:y2[*b> **e:wE s fCxqXE\8s`m5ljŕh4M^[tz݆0ؔhÞ?=K ./@z]C+N'/8]9 <>F|qf9b*/cHp!Y^mV+xV3΃.˸e|s's9r갛7 A p<%!*xRaV::c}h\Ui.E|Q0q\X@.SK8<@Ztu#m5k40(e|~t ֍M۰2̆ r`%Kmϑ>? <gI7N@ԍ+pen^SMGn"}S}@_5g^SKw#鵉r%#_{+/j7Ż@yxOW#=">"9%] p#(+\>-R>M)/LI<*S^O^;2\^ZӽNA]YRkDTXI_r.Q Q/^\zLU 2飤?MIX< 9"Q,0!z˧2#?'rxd}V*B9('\GnSz-|3^UZgX5CR: f.V =>Kz \VtZrS2jTX!Yի)jo?-@<R2: buZt49d$fo5t_h5_ڟ㽭K;ϒ&=7 gGK?~XOﭷzvwlo@{Mf3 Qi|͗F8Shc^3 N8 Qgh"MK5@UU J^Qr. ~w(h{,R*e#Q#f]H<`T)CTXUXN_4y((0DQLXqrTa"իJB--r*0Qw,ЩK,~cOCZ!EbBw)$ߙts;MXq"Α煨$œq8,1-eRxɔHb1LkQdHKTE4:8H.ͤT"JP CEO3k~ W2W*isU crtMbIt ܿ,-D¥ "k Gg $"Dʛ?s7aT*]rV˖/.SOsP|ըIϜ:;D_dqWmfj0(9IC-$\zu,VFs{jry`$ xVmyMQo1;<+&Л.b2XFaA#,3nY]nՠ5**)/jӚ s{I'"N 7 á * M&aSg-ndq7Al O6Q[ Z !1lhT1VnBo5AMbѬFdwm>\ H2XnͬŀuRЦtQӪV6Qg8v^VnҫJͤ7 ePoqFZTmj]1aX`5#MV;DU6mna g0PoV;QD\D 5\>lP*&=L j̘Ì* s8L`Cm duC2[='àYU z. ""l'_~}=A,QBPp9jGrcwCzހg@>M>HѻG#z~t+_ӛ$]U[~@?NV@?FӗM?JG>/G/{@?L~ghz;;?9ҵ5/?XQҧ­}?;{InKiN_;}\3!]z|7OMj HCz5+G~D}7|n&w*?=sH_D_=%rҕ~eѽc+ mӛ!Pgz&S$||/UXb7n' ޷^LZB6TJz \T7j~;-v Bj$#A|-|"$DQ zJ& *J`,F`>oX Y=թgZPTkD&xKYN'2y-&w齻U)\UTsrnnstTHRٸuBBߒn#u_qjjn*tzo\"V{j:+r>_㥲;`ct>asXktD!#!k>Zo xTf<j2@&WlCH+{>n1Ata4W DKqߥLu%+Dz)N.WWHfrr`^f[c[I{j=UpLתD `<_Oŋ9zҩZ&zn!񶇁 Ţ'WLmݬ75"_3YF#jnҕA̴ Ç IʄIO OLun(AP_,J|(^dwA]+&^%CPUŲ9bUwlJT:;n +-]]Yfej$& d3M&j=lw` {|6 x*a^4ktē:JT/嫍q*|wV8`;j3dwQUZ͔lDӭb.Kf*xJl3FVTLeA&r~)ݚZ\Vn/7x&\{q8 V-aƷz57 (xPKq3z,.',dbXUj&e"bYqD1…VϺVq"Μx*YhN5M#_&|VJ6f2dTky/ lOD+v %*9FRX"je? )Z7l EsP ʁ>+bYhvkϖL%\WF7dKx{^!6 ptT8SUSeUrTb ]Q"uN[BB%Wڅ49j)r #VlXh˙t,QW]-7;RWSFphrDy;zwofÑtU8Q.[fiT^Z5RoSLw6;(~<<4a6k\ǽN gql:!V n9ĭ`l4 Q%rI/ DAY"'Sݙ.s\̔S]P\ YJ"]œ7frd]tۃ}|;\|+Z]ÁFā" 2+z\O; wvA?1@B.W.f <dABq#Q+c_w1nv:uPjʅ#B>;,JD%dkgXfwVow'[ɰ] nmtv*0xpO Oxo=4ۻQwkf9Ѱ+UVmURl 7dr2@1ܹ3{[dJmmÝF?6~>Ug|R$Zɰр ޥ|:Vqkꃽvn7[n[:ljq[Md(ZoC#v=/tS&ɠ :Vw2h;w&x2ޚPCJ7nvwVQ7n9h.]ޝ3CG&dߛLFl3ɰlOӻnѽtFwvҍp'Z6MޟLv{n\U 6r8‡*p8whvKv1H h;Z q`d{?;VkݝQoت V{x0vo5J&tw{fz_٫VZnkѸ*Zkm/b`kTt[n՜= B]ڛ+>};Tk7JjT};{;L5QJɤ+0lɰ^zw 60JkhIޤQnpI ,zv pn+Z0ƻuXm'-Uikɠ7mo0f{Wo+b;j:fkWVJhvWo Z'zä^w.Ml`V!ۿ|f}1[qg `PUnU*6vAANnoh2g\}NVR,f/RѮWJ6iԺc^fojw*~ s4hIJͭ}HPzowN&vG5nf?Naџ>ak 0~ <Ȗ띙ΟA˂viM|i9֨Q ̤*&R}2iشJɨ;nKA=,%SDLxx{۾̠3w;`MXk8J"Zz|y qg udr ~_ڃVs7lw'>>r@i [e G> Z.K;4{;=^$1HS3Fl6yMlŜz%x|uAfyBѨr]ωzekNg:my tzϦI#"QŎy67§ V۠JNțj DñY,hfk4+&|j`6؅ ax(ty'r.dM0LVW;ܞp"1} HYb; |(bwg( Xl!c D< evl1fhqacC9v |!ߤw؝riUMOeu"aÙNRɐzX8m:l[Q6i)C1D\z]k:H` Fw,7qiF'PӆCVyٽdB "?|qML8i!=]yߛo?fʄ`i]D+xh$ryLjO"yCnk&@ bcx0T鼹|>dȢ Ÿ&`4 "5ՙ-.ŜMzNlTڽB+]D=@ԩ+t`ت0,H&C2% R^7pX=T(Nţ!dx'f_Gcpy"0J4, &s(9@4X@*gٝ+p<AA Dch< nd8h_S!.hH2w3Α;=>!瓉|"az8A |u 2b04 E,wpuFeI:]Aը5"~;ymfO0?Hǽo"lNVa-V'1ِ]22:\uRk3r&Mm:򇂎=k~<[vf`h zͮff[fh*3nXVjs: "DdCaw;CXp٬kFnrH^W8G$|dy< *Vhtxg9M x&r:ku$Lj"@p4kL2Nن[&^mƓZ f˨PyB.[0 pfw0bZb g>:t(BTf 'pxqX&LjFkZM`l-6w(t6z.o8iۨ+6mgmm.Ѭ_[Y_8n'hKvC m&vٽT"tvlH\&FmxaFIcliQ`YM6lLNa9HܬdRXȡYS5">iYYz52.ZWuJ&0WV)$B&_,ِfԚ,NTmЃ.kuQw3VOX8(\K' -UJl&&|fnC7Fc&N^chT>>Y*LPpPb&C.W0 @Fd[Fn4z"qjD xPx:HbD4H:v21+֔FϱnǚviA`$izjY"_'RP0U,bHܧ4 1` v`ƝD:*ZॶLP@6KL4R|t=Uh5KCF )@\|HĒJPkj `RJj$KH`1{*c|`Ȣi~. J)H3X0) ED@E6z̫;-h%MT4,Tʥt* fVՊrLKd(5Gcxt2e+תt"[(j9Wj1e0s\KDRy=j*xք/ȖKh()sD2MfZo߫tg&#>RyGjJ02DH TP.$i"fi*B4J.F zrsnĒO &2"bxZ/$=̔QgV<^t&:2 bxȗ̀p(Jr. TB|&bؙx8Q[v(|i E!t"G/q|*jR p:LV:*6 2 2j8F0g)/QCx.s0{*7H碉lH$Qit\d(cT7NWl8IhOt2z 3PT[rZJfsOD"GZ1O'"|V`Ѝ2'`F6v`4]'SH,Si4b2b&E"b2hfqbTSY|Ieh*o2_A "O0"2湚D=hr%&2(Ȥ#>w N+J.d&#-%(̢LtLT _|D(_Fr'2DN@h >iUʵd |lJD,A|>xUJtB6L'Q.O(Merd:JSf]ew=g5l2SIbE"-cP$p1)#DMƥepn\D_JjT_$@[**fϦShDJf Sj!.嫅X8S.Ve,@Z es Qozp&^xTjr6 Ì>]hY$f+D<\VIdrF`pVMgMeD\ͧ8syeLJ,r2S08{fgИ\T,XrR@-'/Wto~rApv  y%VHlX DL*(@Τ D&Zd^؏/2\.Ki\It\ !\[(|.BYb~3Fgod\.O.R6<:Ơ@#esL#)CɸWuJg3,,P)&JL**bx%_%#zbF.S)3]"a.-,2xNL.,1T|vY&bpDٛx\D%`2KKb"C$S51g]HYR|%5[pWI<'sB Sš]V$B*e>wWheLcC 2].@Gk"KgHrŤeb:CEl&G d_|o~7ZwԂ@$a/, *F*/=v,p!W0iTںLX|Ɋϣ,HI*5gaY(+tFʜyI!He'OkkܥeZ>_YIVHH_*2ΝcdbИ"`T ri\J&ptR&pEk*BВRT۫BLt"G&mL* :O& gQIB6uVPHUr>A[ft^¡2Hb& =X8K/Ϟ*MRx&[Qg.R9k* eĞ>v?)tʠ[RMcsy+R1.s.Ffs-rP(J O.sd|&`.] \D,ApY,"yU㘖*Uq<*< R&#sbSb\gqr (t>d,-1B.H&IELD<'\heŴ2("p$|6G(Vh.dD\ fRlŋKgي-qT ̋dL2:ˤ I_\D 85k1I\2ePb5_Xdhؔs_\f^$Rʤ^r(T/./?}anFã ׅ%ZQ8l\`7 P/*PCE*NJ"v&lv8 UA^XD BLg.S|uB9`@*i 6mk7vۤUk \&e,!ty`2<s~#Y =nfM3h5w doZ ٬nEUjͪHʓkr k4.paIwC$[vVo{AơP_sY TLٸx~7f1vi]zC7f֠WueѬVe%6 $KdB,[fDiTvFsceҪTͦd:\VF)=53MbZ-z݆zmhwlױڝVYRBk11ΠY_Y39lN۰[- aYLVf8vnR|ST[u0,~YiQmXr5>qNފ+, èZfVqSw&#nh16UZ7uz V0:vV> h7645l,F1r9^d,f vUjըlVq`,Иr  R9x얀C1<5(|ZZgY1;lդ7tFŠәc1Mv|<{Ct&{cM / I%SUMFtv]3O;-6CcxMhiVà70ZbPV,6o(4&Nk=`pxmZz1I2U A.7&TVY6- @m9+k:7A[AZfƺ 5ѯ<]UՆrL&.tͦ͵P=ǦYSi0x׌+jÐ,p װll5΀0- u 1Ha칬& H+e2nZ%_ٰ^ĭy=n ]:vr;.nSux荃"mnYotXX]F _m4~8I1`q O53_ҨNZM&#Z%˧,&O̡xl:S:Wmh3 tZ-Mq>)vW09vV-hAo*@Aa3züPsa(?`v7 FWg8c8az]J?3ӛHWyǦ/']y'QU5դoyw?{ҋ3ogۧo#]yߜw~?}nҕGϧuf闦O?J/^<4W}Sƭ;8-իu"Ul5sfH]ߺtoMF~S`n&Fܑ(szRTZzW{O{޸Sma΀jLmnU+D4{bg}ΥaBm0\MfrOY\ ;ΥAo4'ޝm$avP.OJfo5&A u cb;Zr?F@G[O]4p2^Po O\)䒹l=n vpqd8VVh'[xwgG Er)+:R>_Nf5ۻ[dB?,V%F%EZ9#d2vv'foݝ~w0~֫#FwkX3wG[O={i{vw[VX6~HԈdSCT ƃ^9dm ۭ!ti3X?ZVL15^5݆m7K^&5;OSW[] 2 Zxk8xo<~Yא\rkXOJ?o㲎ޤiGJoƍF%Tn^=ͥCְ{^Q1;R K//$}kVhkzOv>F+|ښStZhk25Zm\tEw 9"WGR7zbg k]\hv,G^Q cQƓx/ybs5EjF>W h{8Mz[όz}QSHb2qua2N{8& j Ƙe* df*HX 5a{;36SԪH"Wg Z >4K;nKa۽jz^pXW/7wZɳTVZ&1⧾@{/7qxoPO(l߮UŰ/N犕|l]'{! rgPF9_(&pv{{23p7l@I?K&BLr ԛܠU&h4LVVdqدׇa(T(0[X9pL&|03F ^+wP>\V:Ab`k:\N1Lb!(M@˥[)7x%Z rJ3/mM0ze=w*Zf-_MyDmZ;Po8}2^5ƤtuҘ!j֛xVT데3Tpgmmǽxo{+V3x PG{0Fc0mltnW.ޘt[z84>nko)̃1vVZ߮W]7ۤ7~۟ 0ft_wg4r`olt{l4֪݅ nt&*HOsr>ل{=} ;Vl&*0w"ZJ}h0(ý˻.vV۝2t؛ƃn g,&n˅4޻t ۅ&Ht9G^qKCKNNMj`rwwww/lK;;]$V3o]~xk{y?3`ǶzԜi=5uTn :6 3A[pgʝ z橂3sN4n]w&]Bic0pdnvaFw5an=;=lRid4 n lŽ'fc$]7lUvH:V:\3tK@Vsg1tj`kk\s4h+˗ Fhjg4+X\zG;[d4iD|uq>!VN=@g0ٓ1;yss܆Oۻy>¤AnD6&Dz|ͻ3n \c=hvj0wgΰ8aF;.mv _uFh{D8AC B*u'ɡt;h~0.Xd,Wz[۸4ܗ;i;?ط|@aDp)\L%. ηt0F c†n8h`jM5b> &j FsNGwxvW/F^ j)T_ס@*UdZ QmtѰ'Wh2\1Xb jī Z*+r \ `1[h6" 2I7A. :lq6_4xZ{sN!WX̦sp6}^lvDZRIJJDŜ[hԻ>3Y!20\Z/*v٪dl=VGncsسD3b+OMZebѠG\4`˖tΔwJzR4IqC2N=M%3z݀)ixP :H8}n'[W0l> &8-tW(fͺ;6m*Y"s!f4v\4irh|NB%KftXV5m!f}9ML)Msp.0| nWau/rCkqxLE3$Ky2LJXݏ,H]x%MD|=+p(TX]e;딛TղR̂nAK]̾EC7hs%>&`2/ t*:I2PBѷǒAllD.](ċFPΧj5LJt`GBtP(tVMcNnVٮbۿE>?n1[+͔~JdV{P(xl E"~\pZ4D53fBjX$8^j4v2] ds{<FTU[R7F/A=:PS퉂c'9by|\1W($B:pSkP{$p,L 3N'3V:=D*4x#QhPt,N5<0bɨ`֫h+7% ~3LVjÖ^Xz1C1-V .Jt6FahZMvz]$p(V ,6ġ %Ckb;`+"(|zP%/Ir6$x 3\ $]LͤjD A7ֵd$O$Rh<]$#B4b{uWF:ukTD`xylťA&U(ű kZ?:68 rrK~rTf9x1Ft&SoV<@#'VBݿ8e/ H ǵ(XVh]|ҥrYɁQivfTDӝIP)Tp2qT4A_k D6VRAO4+a'%ᚷj^&iB._,EP|bX0$ HG" LX&@PA*,`\&}.N) r.pCD)N=dkoVl7YOf%؅bj&=^1Lx0Q(fWNOR.&q/&cÊB 'AK6X*Dquf YȁwEYDϷTIT[xT\J:>G <&^7^LT:ϺPp, nsNLjgrW\V`*`v۹d\b0-@5[0L趪r {V@GR.H"R0o*&SicX z4fDBKe\DRpyլw=nϤ1mNv4Mt}WE\LL&߰ڜv f5XN5,fFX^ }?NLa :v?aSu]`B]/%2Cxf0>g yW&dyRL`s9RiR*u&ݦh6n ZziAPHg2ԥYd7zFͦl-z0!ET!sˋdcb3,aCt6 fI{vPJ_Kzn.!sgX^7Z zŪWz3ȨQ@ ~,Γ 5렪_hիz 3 UB>teWHgZ-Vr&fq8"E$oOUU GwXm.դ@]>ԁ UáhLfkXz :4Hȣӹ!i5MXn_;2]Sy 4ъi3U&d@9Vo0:u_*)B̝RL`5Z TP8lY),.*kL:EoVan[5&24*>7ѪPYPmAŠ_ Y&B'/+pEZin_Rgq5y+1t:aYNпWBz+QEu.+M=fo_'%\x f :O5ڂ[LZ3zNBX:G d h\s=E3͠c^\6׮VgVӭӮkuz̗;“L b/>ըu6B1݅R"ZF+sS7t:ܬQ7od7X58fBt;MAZUk0V7Vuoz}){ fUĪ -HZ- dMe7j_ԩ5zb5 4k 8\6 75"\i ,867ɠO_>w [Ag9^7G."N+:;|o`McL/!C˪^W5wPlPA > UC7`M)]lo]Nд|A,@2$*,å Xd5kj |n;n0Z`6ZmMWp:(qkT+?0} ώ ̇㶚@MzЩkv˦1iVn0Zkxnehy-(7rUכkk]i}0V U5&n7TZ/xA4:!bEm[].#8afӦruՀ[6;ݤ3;heCx^y` ٵL Ho!Lnl4Y,߇7B< 84iP*VZ׏fs:\UpA@<1]1c5`>%34krt7v ϯ dJ-Lۃynzu >Q7} )6ͦ&}y/cR9"kG##gڼz'[ ="D}Ӄ+`\:"#B;"rDyDy?S7GDrDD>pD^=Ӄ߂6<"#r|戼=Ӄ?uDGd|鈼Ȼ@="#9":"<}1!QG@jzG( #b:"gȝ@ޅ<{} /}OӃHŋ3oeދ;+S| f_ۦ~񦻿>smzMHg'.Dw%n]I  I+y{fzvy>nWuwuLMU?O)ܾxRR='پ?٣sy3']htYk2lӳWZ9Я\B gbڳڟGٍ}Vkg;"D*GJ~EɊ(j_QW\?AvCuXqEcEVDqeL jjA2 +6h1|\\ݔ<ƟOwh⃟>Qկ?O7J.W㣄}%}gCzD =Wi&g^;I7] Rȕ|oL|D4C$+oP),ɔ(7M ~_ARiH>IP,'U\{bQ.|rH%H7}XGcy%9c6VDɗgΑpɊ5T2Ks 5wbenEV[(B-:*H!ɚ@2k7nvxiXP`w詤'BLX9Q.}{.B6ڱ|z$<> RF:Z6Z`qhv=CԬ::h!67mFN‚9sw=d4?kzC Zl:Mv?r,h"ج.YF{ 3u)OI*Yś%QYōU=ZԤμV梺hm. "6wfS70Ti.6Wo7+|(Q>殺@/;2WUKT=w7Tj>b#W7y@ /omų0oՋy2hϨ=n5Uc5tm}|ŋ \ ,^nn.LfB%$L(QI<,K&A|| A;7!B'I4Yr=II$N(!ɒ.jq"1)SM:KT1)S$O4IR$/z~yB &N|SI.} 2ddXKҦM7i0CESM6}~2gʲdcYeɜ9_ @A*'$TA/sls̕;?gyTܹȓ=[̙PGj_B[IM>c,rϓ7X@l5 `ϕeHO:`,'M*Mt .Zx%/Vpʑ=+W*edh>dwLYsΓ?p⁥J-W|[3˕+[T4ɝCH:Ξ d/Y\JV^FM&qzU*U,_4(RTd˒)ctihe/HRe+TVV:7hPm_nիU\9H>t~Ys)Pxr[Q[lժuVZhެi֮Uj eK+P,!",XB7jڢU:uErsڵiݲYFըZ Xy4`)tђ*U]q:v֣W>}iӻWn]:uh׺E PCeJ(Z ԬۨYÆF>lAսkmZr U*+]8M#"%WUIv?xQcƍ0Q2a1G <_HCujUI(Vp`*7mݡ[CF7q3g͚5sƴ'3jAAnѴa5q%q-*Q2ԣ'L>k#g[rfΞ9mqGܿO۵jQ,VRz ߳SfYx)]N]vUh3NCݽK4*W(SdusY7oٶmm;u {fko޾[c&[vއώN?vܾe#jX8oՏܡ j&m2jlڶ{c'N ca ZO:qȡ{wK̝9mҸQ# Qaˎ=0cw=tdH ._gK/ ;rC@Zx>Lda06mۭ߰q.]i'BB/k׵ZhՈK΅A ܶiY&'c6l٩ѓg/^iǾ#'LIn߱{V׮F\H ܷ{֍V-_`2xXv0c QBx䍨;w?xѣxޝQ7GIY )sݺ0G\sO<՞'a=+ .8Nssfg.Zi ./>GA>Ƶᗐ +ae߾hp}/^zW^ + Ȉyϫ7>t2r$ Ǽxw}ktu.1/^}_|e_/a?}U<>G2ًW=z?}ƾ_>*`,Ȓ!X~?\ůB\>+/ɣ`A6_ :w5]b [S&Kfpwn ;`L4v~=;mѨNJeJ)dJ:ED8n9>ϡ!PúUK̞6qoٸnJeʗ+kƴ81?4PYaڕK͜ CcںqͲҷ{TPhH G"1G==Bh6]XI-OwޮK; r"B#C"F몤"UvEJWaB+뱡+Fn־Q#ܮ$e#j'z]Dڢ[n}@$ бܸԬ }Mv0r"ۺ晴>*Ĩ*CEfd>|jm 8k1z/}?0YE^hN*II==KFİ/aMh˞#/(IDZ>_:z ˢ]9e%A+Jbj{ǒ*ɐǦp>sKჸk (>}>h}ۜ 3phWK 4j}WI r[$1P9Q!E(`p`- FB-z"5hQc%[,"f*zRgpƁQa;<kz xA[ XpeV _Ď\rx!D[3UoӼ=.|۾vgo sϔ3KU4nӥ/\f ՛żx#dY^EKWm4_{;Ks]\ ?}KëB(R.iدsE+ 4uf&}쭅di,wO%-_FZw; ޷bW`:x)W4+ї08jE-Į8\p+;[/aqb]!o@X; ؁g.^((xA^n /'YfC'awG%9%pnhҶ+/_FyֽGOs9wf06Yqc0Bg/1"IqJbx՛pz"՛w1+ a&9 dͻ#oyF`4Tq2,YB]z=y d2rWfBxBT腈ț,` :rRD[wc$"hYzIXe#'Ϝ~[wQ:MZw=(xyKWoܶ/\J38;tڍZu9`)._iX 7 K;_vQ2k5h޾[!&Lhڍv=xɐ {)"h>>|,ɾ|dmEɍP0Ͽ|b&/T"߿/]9 fM?`]|F4od+o bAQ~Ǟ~Ͽ}oԟ/&!\>|K]<ƋFO"M|o>b_)-%%[j61׿/1mfO>~i̳_agn(û?xHbܛcocsʾBYY]T|!~\B,T{ۅSㄆJ"$%6aGT¾k0R㊬3җ:|IN-0G-> xa;rpBs4+sViXlB z`9zLhq;̙`%eͺ7mٺ}{ᔎ)aiISK4g|Բ|5ko*;,S"dKZF3vkk@;)_y5?Vh߬^%dKK(,n#fTplL3w jV,Y-U^-?sų'bMҺ _|%^V9Eٲf#wkۤvE̜/5H@߾z]̱-39x`vMT)SLouU.A\gߺv9ϛ2znղ XE_ ,'7;0Svm\1=;%Q7D8+O޽iicмn5N]b]AIeL=d=gǒѺFVa0M,qeӨë!ݫ`CZE%;]kȻiߌ#'c#:(,Z2 d F)ťVp)8(6z~N޵}ԀnmU*U8/]+b^ xԡ͝<WKʓ/)R8/{7#Νwrnm[EsZN-W/_߸@̃aʁ%qm]hƸ;6g^CS>}xڥ3GnYpXM)m5㢡ퟗonK< 9\dƵ*,zvּr͙4֒5Iu"x*&v!s s;;RzhZepP0aҳnYP{٤,֓n{b{JŲz%L*{ #|-YXЪpcY1n,U]i vYHpm7Ÿ,6:>{wCt̴vqM'ϑ >wR̀8ƌgԏH {p.F[y-Ff?e},\J>9t8p2elc]"^qyIcʷƔOśBa9v3xhfM=wVFOw孫|_1(qR;yqD=졷9S ߽Ou0 Ɓaf$Ù;mA:jT N@X?Wol߱c'vھuƵxkx AӣdT(9X?C|yngF*C&33Az$E^j%+H$J2ȑ}-{NS|q!X ?DT) r sEBNå^u('A)'HH2Z4I@Ht$$;V]8y0Oż,ol$"<HJ-Uh-(C Z"|5FS]Lq X%jWdn V]ASz%eRs55 @*#]fV*\Xz\ ƕ([(BeA,AKb%K*S\8Tac.΢OZ L !P5>"O;҂@M=hRmiJm(c -<+LL-_beł0(&TŔ8DЮK!Hׄ&9%0F`I -5b?`\uoS׺45 %b#ĕd7̦båUEIPP$IA \JlB$Ꞣ'y\PibÓD"-ƺ*I?SG ~yJpl{} [~!jW.[4 baPbUd@n[7kP sLsGUc⁧ 128xu;xaCOZdOT7bVUaTX\ҁϙ-S' =TRTҶҬԣ@DHW.[* )ReE.P0?2 tEGl܋27y$o|Y~ɓ82OKx>^bDw]Udϑ#-gtٳf'_Ux;GwLmY~3'l\i ^M;m:NM{Nt:GuS>$ =T,sA19tW>ȏ?֒ZZ YHC_n*(ՄZ"-n p ' 7pj:ӄ{J,>4j^.ޱ>ޚjuTTU莺0/67o"3L{P0 G-ƿ+.cs!fp=a&'2%n>0˨d~dHtz rC3:鸘N(jv#gurKjN-Ŗ+jlZ=~>*jvk?[E]CQ'y?ৡ=1xԡ^xÚf31|^z_{&EWwRo9)3_J}|_Vx`%o;\~3E/Du;/Z͌ްuLcύ1.2oeRM Kxtx/yjw67T֙OBk筱1yp7`<n[ޙ9l'; N5es[BG?3*5S\ŷb%J f,}H"?IE8LtSEȄM?n!hx_F 2!"YD)'AQML'[&W -Q\z͠:6n /U&8KT:xV0@'UDS:9{ Rġ?NdSvNcgx/Er-kؤy6;vֳw_xs6rI P4lE^VnZjz+br _x]]<ॹƺU񰘋x;[<\<]vf />e(Q>x.=d?$a Djbу&= 9ȒڒYɡJ5U-2ۿC44m:-&cSC@1WxLjfY3,OVd˒_!X58%e.cVgEI/oMZs=}.)>$BJ.);#͈5Ʌ1+VwJqH1=4<‹&j ҂XиM< гdiV]DQ5j6>Ȱ?8!1b7SzCC i`58be t֮[?ڱSxgtva*B-E9ZVZYN%Ө(c駇7ԑ!#ӐMCN}(?K9+lR 㥵-[/yzS@: ;챳fK0,˃#M|q`%K.]dByi ՕOKc хꚵ:N-ՙO2M3,mM2;V1Ic6c֜y -!ٍT߹{}!ީ+OBh+ N@hqǎ>h..#S[c9 i3gφ={&qy@/a*pP?mt݅'yBa y]cg8m wL;Y ;Ydy*mv;V׮^v0btMlp5B߻{'jCA!"?ncx=aFc*[J:h̑׮De) |dO{tߺ<|OG U(V9bIT?co޼zl0>'Mlci|nȌw2\B **'>s/H(`܆ }w˫*l /i8Zyl򇷦y.ž?\7Y0G$q?ԟ(~TyfS=Ymav~{a#Dq&qcҫO?߬hiŝ4VT/S"NlWʢ/deV Y&E2XSlRjcXo[XbʠhLſL&N(^bh@Mƒܽ SK+E>E  SZciQHSų-${!8r'Kbr2Q4 ${Y2\wI=Jڷ@HByuRKi}k^f@iƴ)%,\~% fM?rPmԭF ۷lX|F @BLy"y$h)V,=ḛ~q0?d}$Sn=wuΜ4jp/:%U=nBdV+!Hn֤-ג9SF#a͊We16۷}ʅ3&߭m:Ue49h`=[.;e^[ԯQƧgo% 돟9~`U׵McBV8 'Z=qlMGSL|pKAٟ13CsDyD9oڥs&qi,r{"/ 0 ac R}$+TbF1q6 7)jLxVѠU'Zv~ևJ|+(H%WߢSag.Zu߱Hb(VZz ;}-HyKv;bEZ9h̴+7>rjCEJU¥5e>,DFe8rB<12Dd5@5F Tq+$u, /{M99`d.o־1ڼ0)-8 Әf]0`g¢H.% KM]nEz ÞǖeͲmw,f7L^C c;1crxv1hT].^u_6 "T3e/Axou".&!21y VٰU~8p2,e/Ukx9Zf> D\aU6*.E•Gd|WݤcŦ݇O[+x+aS R¼o1)?,Ba-^H}KUXakXI: H"[VO_:luͻ3t[ ;H*3wH6m^;6măn"=*w\V68._ .R?mw/{*bmLyit7ny|)@Ǝ^zߒ$zmg y/:N+mQJI*XE`I7oR*5) AR`]xS{"ɱ\ýt/oXJ"hSo>][(|9Q:w:6a#7>_rjwbK"Yz`9CRik?V}(ukG/?>=$|~|'vo_jWC8Me)W`k߾~ 05WeM󧈀sĸvʠ uQ&pF sl> c~ #@8 ~p($JPnz2:^f?ye: ]i|(;nk ݕ)6~ˀ00ƆsJaSb@ tk {w҇т#F1/쁁GXYs &=>q9O R2m~%B ZYe R-p~!rp"0`Qz*D'a.n'ِ_:Kzܲ?v%l Dh,y(°2@ =4P],VPBp6 9C+Sp{IdH/N:`RmS$&`ѷs+ [(r J\)b+L$5I+̠b^+Ҏ% d [Aڨ$yiBHM`F5kHҎ6sט N9E ~&Wb=!:(z"ҝW.Q\a?8ҽkHgw%6Bs?ZbȽt6Z4SʵttA(޺~b{[>oX:@ ^":lʺ'mK:;1N"g7I8<.ocgxHllFF~Ej$M)ؒO`W1$Ʒ-L=ܚP&u8çr[I3)LAg7S5;F$_P#Ԍ 7_QfXQ5\>!W)mzl[YokӸܧ4u՗A+krVNbFޒ[:M]^)&kb(K9DKVG'ՑscE4䋙gQbe㮃'C/I 87lUض64 v(Uٻoak~yӢC/P2쇥ɇ[|v-$ZLvb佞jbEA^ʊJS4x]:BY'Zb^NsX#53 VbH,\&7 LbQiSŇ>)j zC Q1ڊ>JqQ!#N[. "k5hֶs~SG5ȸԽwAcMyR Х{/T3)gB@MZiIF)ʛ%I%D͡Zu6m e-&fFX&(nJRDEJZEH[tԨP(Ԅcm!h=w^6.jE6T؇@IdXć((Z0舚khS҉/t3"U$sl٨>'7G7 ^(xi '|ps+.zJA(b#Q|&9|Y>b9/> b!TI"cKyf18ѱiț8Xdmp.^ՖCA 7-<܊sSeyz[1ӿ3 ,؏R {qb,>FSXoZ Ԭ|c|eeS@埻2)~Mel?0`mWr] /)q_֍yCwL쯻$!*sXT7FTy!h.ҸJ.{;Fpq.s#)}}}=v50^fտ6c"-ݽ42XYD. izn`d|S^2PQ0B^S˛F#agEH^27[Zs~K*c#s( [6]4}EO~|;>S˗,cR Q->kC=ݵ}3^\󢐟ؾa(J{ yj,łT4|ı>z}b~x/{¡߅X{C~86ҠA:F66Qҽu#v=땋QwQ7oշY!LS]enA+.Ʊ`{ߞ];~'1b˳/p(k#^xlNڻwnߦEӫ7i3ٸ>i+B0z&9Z=v$Jp:PǏ= q]hXIúA5^v;Pv9æ]2s*2uu٫lňHϩD0Xy ۷WAOc׃c":#{lq%ұmfF`E 8|{bJCNhWFáf۫['콁;|>t9w.Hy$üMVҥCۖM SĻ@STivz̩Ǐ]={&<|0XvCߵR%|yrȖ/݈rP|RHKؒӋ-iXAzjP7M;1M>sl6; 5s oث~Gp9{YB&N7ftW v ,xZ%q͌ޓ F@֦!\aquMc}8hi7 +̅SJ ,kn=zѣ[ۑX/ZS9Ab]m:o߶ Q Y\N̮kQ2&͚/4kҸaA^xq{ z'שAiauի_^:A08\Dv[Vr*+//*d21k}F6غT| ˗I$ֶZ %%+ZؠRWT (P@#ZV+rK7̛'?83<ƆLl(sdϞ:Ѩ#eb~$Ϲ9vhXژ4q孢lRܑ?/KRke.%61+mYEM ueIoX|ToF$=͕e/T)C3F ew:ō>ǜ,͡,%N+NݿsӪų'!PF&ˬ^ N;ޯ[[tinQ+ϟ=uW`FA:3ER+~QT8 {ں~S" _)}GDu+͙:n[5[b Eww߮m,Yviz_&6@M<*m.]0k@6U;Α)+qeΘ</I;BAX$ ͛9ua{wm߲af!zcp.  <_Zl̒161Sz)mf kWX8˜9s,+˚XOjڵlR?"V:}ꔜ6@c&w!nyYzw(5/ xl5VD1ժVlb:߬\n4˗.Qf3d";rpnܧgt-?gLӬ/%/ӯ2,T m:jLtXY3eH:e$\$pW.Gh<>gi|M&C 7=.dx=)Yb=MÎdgGH}ljd#óÇn>O$X%p5kH =ɬ 6NkQx`rwBEoF)轈>t 6wNɬ|P()tO?'Mbdf+ /Vrnx=]쀨J<e2 Hf,OdHvx|Wx e{)N\ M>ˣxr$Aa˷E O^AߋlΨ{AݛѤn6,PQ.͖)un E]^XQV]E 驨gy;;݁} ;gٕ>eVS<T^4bgA=%!L-˳EJU@؎= 7yƜHz R9"x.lڜS X@IJ|DDKa!C1z()(X\z4{ͻ9w sDR0hչ83s޸SPϱfqOb챰OD|\[(&.Am1{mx݇O '/`\+6)nI?ACxf~a6O\0ϷP=2ޘ0O CLO}nk^W܋~'1rH3e`Я:NW]Ŭ ? !FSN8+A" 9?eR8,g݁ÜB]0XSWeU|f'Q}[Q31/HM̼]msW7[<-<47w _//OnsAxPl<7[d̐!}tibX7U,IU!Kx.,y9(F%sL~3, I`* g8XAVɗr"YfɌ*PG8~ HuPX_>4IJ+HB ·ܹH ab*p88 jyV\2K*Q"`TCzPׄC1n5YM~ZժU*WX|9Pz E43.ԔC ^ݺujժYz5IPTXEF]qr0{,\ZlѢyM7jؠ~=RRBH TwuaY;oZکY&13P _?[?֗9߻Wݻu҉Ԁ>ԃ@͋O TtŊp:|ذC8>t$zP UreJMt&7vhL '-#BzPii15km66sS`j<-`Pii`Z-ΒlIE .?wY3OC5hh8  H5mTozd>[z˗-]xтPьӦ.}jÆ ƔVO6۶}mg¶nټit=|@Sfm"Kݳ{۷"ԃǕ AMzp@4H Ma͜<Kew틷nR ܫWHO>ǎ>@+̟9G>bكݻ{[nr=WI>PׯZ2oRкưHy̢#=|PZk4)J>ad{Ξ1qt#ԃؽh y{:vhMk'[׷ 3IJa0g`00:*r>{_I?;V}^ J1O7J$InOG;dc?C(s.][,%4!ׯx;Q".=q)% Ńw2ɨ3h~+C9% Dl_U}zgzE ;%_.;? Tz"û7#h-koWj~gߞҙo_:䪅to_mf~+q/^\*hrt/2Sd_5篛8xi+ٽyZXO:}^> KUy@\Xw:Q@S=s?؟/T180;7 e#$ϓ0B$G^9BȓٓGzbE~( <VFJIǎ_U ,\:#̀vR{Y-Ӏed0ZҜnc!2К$&p+ʘIRE9<5VMw\ێdL%Xg.Zm'|{Ԋy ɋ؈~7$]@V^BnEI8-$.ܶ9ol ac+m!D>r_ˢE4zlE߼E~!QKR{b^ k6m,J+#8k&_E1q,?YdyV U2GE8l G%;fp]O ݇@|/YiL Bq&/`厽:wٺm{ $ϣ1_"+dW߼f(uxˑ"C*{V&Bt>q漞zg nUjS 3/]v=~EtW-]V̡;>vȋ7ʲzz I Sv xuwE<HGD^KF Y]٧@Nک^|>׷Ŕ@og<.qX~]z9dްeǞ򢚘VF̤TV3o5lѾ[#M5Ɋ5Pv9 %QL|slTNV4bsMX>#X|N4/SF& {Q)C KnNEL 3zp`55nپK~?yڬ ,[fTÄ/PdʘNoӱ[KYت5XKD`͙`nдeNz8tĨq&O9{p&LZ6j֪mǮ=D5VV[lXH/!._zPM۵G~1O%f A]1NE$t!LkmиYK !azF\]P(\,LJU ۴*d-y9\6qd?_A,k`&ZneAGDD??oBEDoTE[N0I02 {( b5 N*z fPpըl94a)~z垙vCo9|u#@)ig}5X2ժQڐI=JsRH .3 t=Lhaf^4KV%thFCSTh\?z\*I.M$3Qy3ár輠:$cPIz of#%IƪE/eeBO Z8}2b HRchltuԌȋ6$m=Lm:? F>k];}pgD=ewd˱N! A5똢F̰z/ri^nA4s*[cTs(}, JݿcE'ԫ}49FP±ϞۨZn߸ Dݽ1CvEXEY"7k՞yMss ~,3X/^2\*_{Fz b`͖#/d?a:Dž^rX+XǶG\:;7c} #`s,OL\)0Fk 9qxS9J"DU..*xIrO=g;gL5hbNk *լegΞ>y}nXt ̑1Be'l:Q`Gi=m֭\œe9Ь|@M: KU`¡"ҠviԣD9f8q]kkv.e廤)ìΝVi4K4S~鿦W~ sĄ Wnؾ,g2w-I,ώg% 7}ʍ;sBmT1oH>imgA~r<5j VX쌅ZGQ|]NYɯH#!睐ifsm J؎qR O1E8} j|8e#x2#o)*$SӱnVJ -U4[U鸚Ў^g+!SC& Q2!)D4 %/Ͱ\Ų);qj6fs\!$\ C*6r7n:YR $>SC$3\0h U!2ȏ"3A+BThD En&Oa#\3t4q4[d4lvI 3M$3s Ko?J@DCvABl,,KlVU0e#qƘ3tNf"0k~1@:Fـ?y/`˛'w.Df<)i mbS.RZF|ysT{8l|LO28,^i)h7TTJUKY a !#!R\7DcGlk ]G3808DG#7lԤIӦM4nHU+!T Or[%'[nӶkwmB6H)n{ԥkiD1vlߖx)ծY 32i^}95uHdžH sSqԻG 0AZ4tjQbĨ'L& qtEFTS MT1~Ơ!c0nC0tzPNk̠(xi%꒐ŋ̛3kI1NjB{!{ک)Uמ^z%"ϙ9m39Act%nƶFW,EN\wE55A[๲]cF!&rǶ-ׯ8ĺ5Ʊi}1qÇ$4Ra(t;tvftS'%Ыݙ:dH_Sg]gC@ essؙF@:H(irѕ{wmvUT*̹KbQo޸~,B^bXl¢bUH$#ȵܽc܎ J" &g,Í^r==!f}$־U {*BusM-|ִ7C_gIp";';7i@/7~OT̂]dNpоAz߾~g8QsÖ7%oZB-g9z}c_/qv{o9ڷsz:oʬ  b~)=ymcot,NPXN9Ajc'QF媙u;q4IzP_#^/A׮ё^cҜ]qeY0M0rRq.^_% NĚ~n,[w<~z*}vdD4HgÐHZ*Jgz:XcIulʊo+/tzi%0GbJشRTtXг9V5౑ǫ>Aul- I[}\4+Y 󖜤TxIWa<-#s煡UQ7 4KӳZthݬ*ƛYvOH[jRB*mʃzwк)"Q夹]?f=:ILbL@)デ٩M (j7gCԺI]:O5,Yjx hد{]K$<#h^Jg D ̝6~x2Pf{6 2gE}pux⠙SehL젯rC;%yj:tVOiQ 7}ML=c*+j{ʐW2F&IUI~.“h1!\P)1Z Dظn/Blf9F_(:G%gVzv]PZp]<cJ6ښ40pjߙhy M/H -Q"1f;&A?.).5%-X7-S\'vGlT*ro[R ?-S`nExV'(~KNL߲p!qX}KP1%d7 ]vfJ|&Ej\Y/"U|Î' *(3мna[Plڳ% ?YS,O '{kCD i/)818GSKM7=xo@8Bh,.$sȄ0]5Ms`ΘvAuAj?w!p;8UW/f}Sb)XC̽ȋQn4Ƽnc **0Q75?7Dw vVE1+|"G]#ݦ`^٠| aq,GL֛D߽yݼS ;7dhJ@fO"vyBTM7{"8{02W k2]ז`@+}`n_t\q`ܴBf7"Ν:d)kC,>0;SdS:+vӻ ;q`t$c}>ybGwmr>4Û3Yطuf)>}uû7ܱ~9oФU,ͫw:8w :R8%T,)\ՠyS.em_dDEpl[V/1.>Ͷqżf[t{^4cܰ7ۊSGߛmI#Ͷxa}ml 7͢l3' ]ֽڴCzuO}Mؽݍ^0_V}:lCzwlڨ=56sFںd6wj(6GupɝP}ڦQ-p0l4~өeebRɞۈ{nPV%Tx@cqnnmU-#[al>a~R.&6(+Pn9ݐspCmT4e\caDQ҈6g5+o}\nmՕ|6W\q9vmϟ)V 2D7ޱ;7\8SV%> i'ZiC 8mPF>{0ݟ3yh5&kzl3ɒ2}riݤ"cݱ[ri7MXbxvgË_H** 264&X5)%'ݕUNW\% ciJY)Ӧ/t㦇cЌ߀`]`H=ST59w'Z+ :_#TZ7ӁA+Y~6Sy}/HN w.<D vK 1#Fb\Yҧ2V;aPژ7ԗVen}ļ*zGyLOZISdU3.We-kɬ&<Oy:ݸW9[Jh7.c|XBbBy1h4hJ̝q+1tc=K@;:.|ohIY팄ʳ퇄 Ds}dsֵ×qb+/^8s(/\$+XK(WqkRXR2.vL^_2ukı|ɔ7Wphɕ7zI+o9[**P:];gk’Bw"MU4m۵q/_mᓡWXO_ÞgXXAZu=xًVmܱ詰KWnܺ޼!JACM[fGO]RCPNOD`ɖ] q9R3;iqETPB۲kgχ_;9Mdğ5j١Gacb8F*wLgdȜݿI>FD6nM 4 㬇zظE./Yn=`2G%J+\J4l֦c>8me8ԽQIOsqDL.QBՠMc'ϠnݵOH j>#s+TTy5kݱ[CG2sb63g2$Trpbz[Q^X>pu34/ !u:ݠi+"$ʅ(@,0@Uj\cΏC _&*Vl%1}r VXFL3x?zT4R(q@7"~1AHlUjZ)q9PtXH) 7q= B2/)&$079 2,XDinJBLbN]@PNQ#0ԇ^$62'QVlO[;͇UIS1TJr(^s)) :2J*IkH!KZjn@ieTx[{pJzc!i+>H]'II}~@,uLjhfW.>& ^JIol2>鯋XX%;֦_c5%%R(ܮKrb2'v6B"#NEg$|bXCԊ0󄾮W|w`&W.؂|z#''A0qQv=G7~n\2Ob#OT/ yU.>"?nWjG:#yt:q;):zԿGCy+7Ч/̭g#- @CbA}+Ƚ=u:{tILAw<”}GN i5>|9/MEYeރ4+|7>%RsuȫϞNI1~kf$+ƺć@ _/N?y}6/ōzC3pfsN?}Y/M-=Í*y >c΂VZz"L4fXZ4d]lqr|тDU8WM؝7\rUK d4!Fg͞=kƴG ثK[dyR "u$ y[ ~{d4y$Ыk tDr3`"lٖ9Ju:xAzviײQH25/q)]=8h0<ڶh\ 4¿oLܖf*vL|zֹ}֨\`Y xUA -Zm߱;#U=mPFr)נq"ݖ}|j4h) [lѬIzA+-iŬ7,;(*X|j5oаTJ2H(1bRCxG+]bj5j bAkըHTP&EO-@%K_DgQL6M3X|.T(FbȒƼ]|TzKͮ@#3 eB=Dly\8}|1|3) 4ڥV>:S+}>>XFlO̘Vl'V VSXE)_-J|ߖ)Wɖ?OҔ/i:Rˌ!a?\?~ G2Y~7_O3˿g YƖEg #T=d6qruRev2ΟĊ;Kz:{õ %QF-?OC((rO wordgrinder-0.5.1.orig/extras/icon-sized.xcf.gz0000644000000000000000000047310512243251035016372 0ustar w|7~LHثeC,M!P@۱e򞲶>G{mm{Ȳ(O_.-# Y$sI׷}}Ǧ{Ϲܳj.t (WK?[ Zx}ņ~uX qÕz.ޘ >.)*)DW&iy2W l^"1ss-qPSWQ),+)aWJ+_w7]RXR_粘[ȏ~ÓG<XYJO0n}O=\U̫y9XjA [Sw0}??_ 頶~B`}D¸D7WX}JS[%­w=DwjɘW]ynz៞oW_YWpّ5a>_"yVo2&Mfd!H,wo(pw5(#XDdbB4ѴQX+Bw%JmEuSO8 Z6ά(ہw|%Kig~ĒA[ȅS7VmFG>*yFza=4t|tŕ9!i 7$7nWfEbK_&Y#C2A >8|r챋B9GgaY,&U|+B # bW^'*RVSը(GV5b?Ub]!VH֍C} !Ws{.ݔԾ:GWGê;ܹUer-CyVAq/0uT8bdAǬQdm>$, CoGOCg)/JW@ϭ^s6wJ݈ĩ3xiK*J彔Ztml]Z\ߖ..~Հ~s{/OȽOp(^M..?O-|w<>+}_nAFC~J}^_L_ơΥm'Y\ÆwaK^}t*nOƒ D"GǎF1}9QSS"E5Ǥ ̦mi#>H"/,|(̟rX B~8 o%>~0jNPgl]CGia~-@v~*y3?~%>a\I_7]8!Z[-0hg׹+94{S7=z=EOk],^X8fT) D}??@נ+Aˊnw=n:PdLg^E[_GP"fѰwL(w`%#9cӱ#G[LX2TF <O5a8-֡h(,2MS̈́ep"rlœQ1EG}H": `o .i3%/ƒx,8v^@ksx/8v1N"PH$BVnD,|neb)EID#bD$>#հB, eG1$|4bL,xKe= fck2J =1ைq|AHhnBhaˆ]nw78 w92Syf=UDK#E nXǺ2Υpxap*|n}KC8*ĂF+P&yHwx]6T1MpȤ@މS=x&cgq,>[f"t_0gK;Ǘ^z +=1=8Xۆζ?NN(U<$qq 6ါkv#ڄX}N.;tAuB5spɄBYt7(g#àါ$s |Q~зA" 'qe5bq]u)PkEX"&Pm~Hڇ{襽~8Pbm*ISGLej2? ׫t;q&#O MHfUJGTB*:Z8h ތ#V#G,.G;ǑVaoup( k#/,ilVv9Q3i% ;qhTRH-#@vH䔐j̓ݨ_PTq媴O7јj5+,$& Z"eDU ∔ }lӨ &<.+xb(I2˨RMjIBVJMgKĤrR*M%*hkI*֖: t4X6 sNw&5<֎vđV.W9.w#;X"{hnkZWH幬jei[@Cz* (?.":+(4 ՜j|elcO.]j"פ.Lr~rʫ,.׬\Z:ںA(i:ij}4֦._FU %^Cߵ(NmH]9Zxא(|7X?(.ʼn A3=elx/f~j.u`w͗foxmۯsg:߲ !ۇI'}׮X{˄7C[._[~i4t5hN5ͩmVX ^޶5a^_+zFkxoƾ]Ķ0w*wߦ6.90?4W5oQќp]od6ءL*u[ئ^_{/Щ{8m\{^p\{^p\{^p crO<~9w§nʓ Ϯױg~4 NGVZAί6]WѸ9x k|ɤM/"FcբN`ïW a_^I^o&F r<4Vfi Vbtv Z:&h36hm&z'"p.P895bl&XlR&q/NSAD|lɃ*gLr*6n5!9bD~hQ8g6hMct<4IX"]8v?: Ft2KDB1‰޽-pb ֟N&fsGL06M%‘(b&K" :qt3%>5::qmwz~O#)(ph[L&g`#I7Eއ5 \>W =plzyP6V=pDi3%c(BU@Vc6wNG_ Ĵg/O&"};zJт]wsЗ&̽i3#GyikC;9B<4f<9̂>:ut$ nb#lw_/ w\uG vo" ?=xcGlr2@:w< nV[H$nÂ<=ui>ܭ~)Z2b‘#ǒ! 2?øn5ԿmAߦ/hWEd߃.!?(ޥvjQoC-$7nd?ulSQw=VK/!fBQ&T _cCM!A:ZCԔ3B @Zz5;:{q Ch,ޛu(bғHĦ+`mDd* MP<=|*p3q5t rƌފD&#d|j2pSiRzXᑝ%N# 2Ddj QXcdx(9d2ch'Ƨ8~ HQ0Y{ɱD!~i~r|zKN& NTcjl6)Xܴ@YtSZ&X8?+KOF`:kִL 0D~|O=q `fQ癊F‘H"Nd#ɣǏ/N%Bщ >v4^[f:ɳ1X؁>90I>ʑ#bd<|t~S :CGG?rǏT,h(.?j=|9r}7sD'N=4f oz}5WpBsOסѥ;Y8߇ [eeE8s1prT,)# P$H%rX Z!djLr^DbD*z9XFRJ٢HB|'c"TB\lDjLH%T(ʅj嫻Y.aB!%F F.*G/x}4;ZRE%B%!9< κVOz<]`o54./hUKEbqk+R_Oo? (QC1oq$R;]^'v{o.N( $n_ R]oK ]pgTxzGz;>@z2/NU +yuuveWgudOaz2u6uuT<9 -Ț ua 1$u8Jb]>rްàY!e= E,k1h*2 f@A'ԧ]]4 `oݕP Oqw'-MMN>A3C$ۛ=8GKQk47u=}͝* [ڽ6m^Z۴h(H"qC;Zr<64-] L@^TXG;6o߀A*W_OMD0h %Bج4uR WU #B b\zr@b|RxCpx> R#j 4w4w}x{5M!WկàIu /@Z~As%6ꃍaȪadS[Mo榽6 N k0h<؎AS$//oW tuEpӏPozT&;[4;4_ڃAuj܊.m H*n 3جgŠ~--&'ٰHVW'G~ x?A葮`k 4:>:p3bQ1'm/# ݿQ=A_P|: b$ Xd4 {`-?w=qDqB~"T=@bL.ApcooW= >Ph"2O SMX|;<٘''"pdr|x<Ub c%dvt3S_CJd*: f{pl0LDݹLO‘5g=Ĵd 5|ZoA ݈Qs Kzu>":{y+ 5ޟ.@M b&Šadsx5^L &ybl.^ĨΞ=l6L|ahl˺f3sjS, `Xg, ^ðI,NVdsL6ΠqE2Qf1Ic3Yldug/28Eep**P^Ű)U L΁y!bq ʫĄJVj RR/ht&+6@aa )S:Zi[L(NTZOqo U$5Zi ZZP>AGj ۵ 7 YR&U*IBNVm/a\AJN2ZZۖMz@):T^tR%2BJX}U3lv/Vytzy %z@g'n.+ 7ދK[YHnwe ΰ,6= #ZBN( nKdJy)6}[[{p ͜<X%3Vek`N\)䧭np:;`a,hV+@,SKyJl5=asAHOT#%@ҧ0lnH 9Bt{6eLNasT)Zsyj]I\8;:]6c0jBunh=F {֖j#:IZ\>. 3X]ŨIcgca/$}so|4#_*n|Wi_B3!E1T `J9pME|(*6(TvNTj|Qt)o*dnwC '\%dr8\R3q-=QIA֨*SDe0YyzRڋAqcP)H5 B&6Áb0KRK(6 jT -y^|.[1ZSkF$-Nt^10s P=QޏIҠ)ZRA{SS!IJRTJUFR.S@CAR+P`P"rt!9PzF#SbHdcGQ:#jk55]xjm,nSaQާmz.Mikkmohkhk8lkos\lLMS;ZIy3 ;:= hv4#MRTuζwv67/EGj|`>wm56ut^k푭XjÖ[bvlXy++7kvÊ+(`ЩvGVn\f˫?MR )gMiZ B]+m՚+WPWɋ@t)sz1ݱy КD`U+(i(hK-_~O:bʓ棷_IA%uzz'Z moho=׬FS+'_׬]99hc7]B[PbjKK6`so߼5]_g~5YL]_WZ'V,}~u4ԕ6u#W^#n2 *3>*(+Naa.vhƣoPV}k_Y[L<2JJ.Z@!Xr_R劷Τ֤rwME'dIv$\xث|FLw1g?< t%%w_}RكcwU/@?Oƣ&?~gwG/}g~mOog'A |Y ˟LTOނ)i(x,Ew^|lN0o{wYT`]T7Xw 4V+-ey޴ep^t^ßq^s*L~oؿ땧k7__;2gJ߱_b1 2e.(\<74b>hZt,RR\fzony)]Ϛԅl~ZlTBLoZz8 dSe#Lg@}Re~{']{J/-?ĝ׬E(pa1q;osv=u&t82;7C7vߨ~I~ggZOތʗH>/2eu"eobL]:{9rLL]Nb\ [v6/~kec/[o߿z蟿EY/'ߜ>+S_-4ëW^u+:OP^:Oh%5~5LW^=0_Z?Ǚt#T]O/3]5`l,J2~@i49|]'\kZcCi5S,NZkC/_ә425-~\gY:hu:-HoZ[]i$@FI5+I8of Pzӡ7mb2-VLs:uN`( zԛ@fƂn@?gip:*Rn`Z#)TujRcmuvt Yo&4V[46h% Jff5jlZNipTz[[[AUkfݨ3:NhQjvaYg[fVS& js԰dh5Vkv8Z$/$i36Gդ[ZBkqM6 ̚<HQYOvsMΤU(騫mpvEr4,VG]kbv74Kskj(QoZZRnhh6\vuZ[C=Y[kkV鰩ogzkM6ͤQ( 6ƩS }m=Ť5XFJ؜pYlkj_7{X_kQJ&Qgoj[Ca8jjk fЭj1ہx MN`} :JٜMΪQYjuxo4c-f^qjM`;lۂv1klwC,P'-|yk63imkfRc ARVk B-:kj &3 jXo5YJEk,faT^i5ڬh/ á5c: QlNEPhfXa5Y@:KCnV+A"-SHFkӉ͆wfq:p+WAQݗ&ѢkL:f[[`;DG;w ik kѪl^N/PWiM f4TZl`3P*ɤP?eS\h2&ЗmdR-&`ԙ@zG i9#ӇDg3Sb?Ym\; LCl6I8jmHx2|(*Xd;v::|aw&3%-5mHrzva{(1 E1Hou8-46VJA6Z&̄D(4: G'F1l5f%Ig9 G]c}w<ON'G{$GzP8>  6+(V=“sɱDbf.  M\8zpf:Nǧ0j-vW5,o =Xl<$fg`FCj49k$_`6 zxfMO:xdL<<94 LMEӘ/bkښT,Է,AD">> 'fEhlfj*yKEio0k0X###Ɂщ0k`378MNXB|;mP4;>=: wN%G"Sq`[ ZSM#MPpXlujf]h l3&#hdp1Z"V  1T*E U RHmZTV˅J/Ke R^ɫq-^Q JI5zRKp-*TWVBMj"D&%HF{J_$ ITZ9KeJX"#Fi+ѝr5ITV *L.W2aPJ TkzDU:ayLbWK:R.RP DZjjU *(Z!e2B!IRPkUCB)ʅ*:%^S+ZRTZ&&TNr~u5_(&RA! RR]ZrTK*rBAJ_R.UCE2aEIR rH(%24jD|WQYzP*5)+@/WJQ'VjLocvFTAaA*/+i JT&,URF*A$M)/R' qUqDeJ TJX[* GҊrG+:)oUV RBHqIAH&5h!pZ B zBX^\,T*|B.Ñ˫Dl@*I^'TReV$U2D$U|B%TrB"*LokUUB_,U(ŕJ@J*KDR)ID20BI%RT*PDg* %$8E]ei_Bh!!>M5hb%Wʪ*>_QUQ+/$1sADR.(+5UJ.W">x ʅ|nD!*-%y"5&HŘjRoz\U Z!JR.EUؗ R=^T5: h+Z%QJʫX"Q@OJX(RbNhs -f#HQA |,k N?$,-T±Ăjegy4$I)bQKF@|~U+!-IDBX2z{}mo`( KRRQ-Ʉ DPQ!"SΖ掞.%U"J!Y!X&JWyR%e"Bk;ڽC}=^χN pЊj@F`&Rmwu?u}M͍OC3{IHJKR>$[V,Aoot{nw}CHWShmy;=] WVFMHfP z;Muvwp}Ƭ_o34hs{[:=wX?H1I,}g݃AO+0v@юSX)UJV.(jLt9\v@)F"5QrZ)S7l tnkqaWJ~صTʾoq =Fo|jdpX+ԨŕJWKD`]B.v{FaJ=]?6r4rV-"ȪjpH)d|Wc [ݣCCã퓱pp~\%TI**jL#*-.N_]c_?`oRM )4J@ *,勪5OqGG}>(qPVYU] `\PC`wgsG_|bVGG! ejeU Rm,9 }923114dBTPX,(d+*\}].}$9wzAo 7UrEuqqQPU:g6X##^Po@wwpxu)JLzDpwwy{!\&/u7-Օj 2RJT^> tjpxgkˋ:Xa4BW Lf?>?4 u@Gk[g+ "F h(74>p@qz;]n7Ѓ Kt24dRA/0 ۼ൷|ߋdhw de9ՠ@|j^z.G  tT@}5 )oFV%BoW`'cwx H .z*7"}"}aCL6Hͤz'Kgryy\(-"Xt&[ɢs8I9=,* px ƟMde2 r虬"fffѲi0}alξٿ,*WDgr@LJEl,z^~*Fޗ΢fLR8;W!R67R8jY vmNO`r@q΃, 9IҬ*UB>% UPUeyqN{~65#R!LEjP UUJ5a2E*TA*åSl )PYh$|XLLPШ4չEr^¬y`o\7;O*BJYN@'$Zt"Bu¢Ԙ5ɃTS!A屹7*U`Ё"b&e:R_k*)Jz?xb8;`,($|! *ZQIbJ4A 3\2Op?)ejDcˠTRnV*a¹˫(fC82CbqyiZhPK&dՕzɁR*/?ŃGؙUh4V1u{{9"T4BJ@ ѩzN^>ǥfs޻%H F(5&R&6֚BR) jְT!rh P0,ڵM9l4ZzQ?T ufq٠ N~N֞ߦ29Lf)GEWFN.Ae]Ì@@3'Y-i3CY%:4jH$b(Vq=D53r=t@];TkWȌFDZ S+BmHոҸ\*$)+dqI%7*$'Hɨa> w59 FXkLfKbSjn1I PeK,(2dV򘙻Ձk FD,#{Me38ܒR={7rU w= J!@!$dnK.r}F6Ŗ޻%[ڛ=x}(Bz7/kιv9g帒bJQƞDT? BTಃà&ahiUF1S3|FɟTJԭC>Duo`Y)zX%aNGJ B6w4w uӺv BD)Ur )KO42) Gdh胔zc{('Ժ/Cu$;puo8pk#\+eZp؇ygޫ׮vfcWޫT¿׮#T`}_V}v}"nQ{?ez-ÔCzhz7?|mg;kpZWuߗo]B  F~WvN,_}N.Rku*ңp@Q/?8@Kjb]/~.ݵPWwjuu^?x GThxQ]rTj~~x\:С:Wџw9'_?yu?r?Uڿ>z!H8?NլOO?}鉓7\_ϯ]}X;vo9=~/~{Ѵ?uj2D m(_PŒxd3&dh"R+74ZK?UzT,&yڨ)3"d$rS&Ea ){h߃*<7k?QR!_F9YЫZ ֛JbMf,*9͔uj5r j80[Ma }U7MƩԔ`ZQ$95c9Jjr U E#F6\BXS3L6<ެchOF#4PV(285=f#+ѣZL5A.H:lRG;`"ʬ'QNԄ8m%$řI':)_"-SF'SUyh$!ԛH9, 8_1 F`jhh ٨7[ZW4R.rb35WmS&ѤaB (M&Q5)^k*dlYJuvqj]h -rrATr  C5)U+QZ,&cPk5&4i@tYAtz%VZ^"ꧬfUkfu&ުWt ziV+𥖫 :-\jY3rё)F2LMIY݀zit7Mi5f iiefZE zJF#MfL2Aid5f:ޢUZ*,zZ6niTHLQ+# 1WSYĤ2 9gCo:Tg5it&-Ng x6/2Y1J$j'@&bCƩ)=1BɷP7IJqY VM&RnzA!bj}m}hLͤBm6Zvl2[j`0U\ԍ)k*% D2^ T2`MȤ,Z4^xQfs߄`̤#Zc̘!-I%+ @QZ͞u+\[\jh1pV0~TQIsVR|祝 [l嵗˙by{g-Ph!`Jn4k4j0TLbƺ#*B`w۪4Ԙ y6mf 6^G+Vu Yo [˧ܮb)Tt"rӧ!/dIB._-Ùޅ+`l-Uҩ,z?=s襚\.MWB5_B{+b*+dP&WB<D_NUFa.W|+F̠M`"W(t^)Ӎv+X}ާ r%Ɇ1ʃD \<^|\>U,mj&YȤ22zC%JϮ+J׫l.)lbjRLGܞrQ' +vRK aK.l&׫/m+[T)[-ʅR:SޮbB.(q~k#HBr՝P Z}G{^~J%jwU+n)-rw"r5Ynn+eQ||gwwkc]b;/V[{ۯek^XZ J6*k T&nQW^#LǶRNS/t>.\}Z2F&ϗk˥Կ\٭l3@RNU0J1zba~XMlJd0Wݑ3r^)msd6[ʯ&V͗[\߬afZ"RuNu3MIp E@o]tykZݭ[lW@kYd _{ht>"[loommdr5W: bk׷oVսBvKofҙlyc~!xB6Le[;z!Mb~\z I_*v&QMPioX*eKړIv/lnlVa-Y/_)V\6oUnX%)D|.,ogSn.Y<켲 .ZHr3P+oPb<|*moAolmlFv.T L>Hf7T:Yͯ``'h0~u}u5ʅlic3b\(v+R:^&ZVb핢9e!;I'rk|_({j Y+eiʹJ ,2+goy>^[V[ʛ[B$YNVC`rmIet`,yJ.WZy0 r6t]NF;*̢v!.x f;4,"UMU\oe4-OǓl,W7f. s]V*Z>X/D,lyͬ/ ڮTl:-lqy y5*l V[[FVŸᤣp B KCu^5Ol}}sܼzǔ)o{ɤB:OBaPAJExic{3ت=kX".䢩&+/l3彍SyNuѝOJBƽ[N7,̯Gzu[`)'Nl'gDaK}.^ڨdӢIJF&1 i۝#]B![D]0P^t=c'$ńC^צ1}N!èػPѯ'GG'r >%匍E;Q^4)%cIq+RXcO>y_,Dpܱ1&If%.2MZVJJt Dby+2\& %FC*dLJHd1&G(4h)EQH~%¥,$3ɑrohq&Ѿ *GT Y<8O|9ϕGF"!0*0 qB1kΟg>:aRYM!|?1 orpK2Wht*X&'D h `)Uob[bDRdNN?J9J$I#,$[q9.%P\(02hFoJd Db!% tsx8cl\˨?㘈Ȥ<6O,`|+`sE1w&D`^Bl&؝f},P"2+ DAX$'I1wb|%0Wz`ИB1˽'K`l )e @ GA(>KELNXc"Pq?:)^*9c`N(WQ 0ktxt$IEhCHJ190覐&rd59:8.BT#h$Eb=qh˓&rB&0-VH`b "hu,w!+ 3džٸBJB@)@O˚f</lHBG#ŗrF|]^{"]@bt^ ɝs`)` aB3E {]Lx::-!/zUcqP<>)Dy928>)Pb\b BKX̻lwNH*x<@LI,X/}L䰻gwO3vv:JH$gJ wrBdv㩈w٢ZрX0nhW+ bK&cr0ɄD z׽<35\ Pr!k I av15 hf] S^r.ג>Ӎ!jjfz圝[&X kini=Q̲/'HvKsN}Aw,e7X[yvF] +.ӱhZ0I_CM"곯LCs÷0:ͦ%g_\cfO..cH{QR;oyר9g s6VkYÑḏ>"9z]5Mz6huz f}ű0327̸A>@x,n_";WF;P$8T2[rc{0 hg c^3/x܉ǛH i*R{~3F#X2 Fv} F˂%lJ \G++ސi@7˹XD\+^.^W(z8]FL`]ZXv}@Liv./zPX\dv5 8D"rzZ}.js޾C}錂V9=qלa_Xq,).,MxGp,[2=iӎ`zl k5-T R^'YJ8KF#q &0&Ͻd]{GGNl1 Edp Lf>4;|ѸOݳȦ}.XRPxh?=J4G7NmF#zjljuQvtԙnFw{KGOo dGO?B+Ũc míh'#=]֮6wnGѹFWkG6z{kW_o{[ ԋ FϜikowumxpC}: 1z{:;=mg;Z;NAZ+?3>Eg2h gZϝmlѻzz{::Z?Bg uҚ 9zv p`xu;[{l[P鶞OiꤷwZΝ4?7[Z:ig6z5x/t?MwZ^3z:-m݌SghFO0gC#wtZv^FOGcf;ϡԨ^FowgkN@ε֮154 nJmNS:뾾m 83P^F[kKoWc7|4gp1<@ci.q/0z`۹Zֳ֞MMm]]֎OKgoIk}zBomew1-AF@{2f* OFtwx\(9jm8Kftuvthͧ::tSK'B.p|lCc0_tu4kn쥵u[[ƦoApo5wihn;wF a: &P+(ԭ1I5p>4`tk=56qϵ4m{H~ 1{ZZ63&{ 3 LJ eGݽg; `]@}IKtY'䘀Yuvt4;[Ѷ֖޶3Zh-Ϟa p'PN X!ev܍〞[_xڥьdvlX?_F"!1>_P0!1wVCkniyᩆ!+I!$!|zJ;&pH$ϠԨv-!RssWt=e)76H"!.ӈ@(ݝm066i`L*`PR`b)H\BI*>۾Ɔat2B$_S_s66kY \&d"gQnt-}I169&MLԤh4^=!$bWI]mLPn`B&Ϟb~_?dF82`LrJR-bx=17Mt{?72(%(K-Tb?D\_)yÃI!_I*HgQoRLHH spb%ᘔMpa LE !6C(*Pn4:c#B&K* Ȅ @&'e Z$ZQ@ZqǗUr\I2D%dik9wBjKa̦gQn$Z0U PYLWIh*XQ f/G*1MLq%op*BN̄T2XmTDB#E1΢XiN$E kHی aNt~r-W%*R+HJ5bE21op)0 'K_AIdJB8UJJD#q@JJ,ViAgF&ISF#)P[\Z}Q:Qa"/RF*bѢUjI*R Ur JGEr52OS(Hb$e2.S&%j\HbPJII 1g4R ?J0~/3 7[4Zfz $)`\er`0%$J)B1Xpx|HhtI6ihTb6"W<*e2L) == T)0NG?LOL*8.u0K8,O."C/m<$2>%HIفD:>!xPpB6Hq .&e >JL8`?&z G(7j0.)!\Iq3Pĕ1yRBF&1.S 0S,x!pf@ RI'$"}KkRTJ3_V$)cc|\(u8**>>m<;<5̤΀kЛ80`Ls0<p|-+ՁUc暸T'f:L*`2!EQR$ĂI>*y+t1תD* R,̱q>.s0@ ܬ1<ݨ7#2vHo4+*W[|˙Jp_.j`WUJvYP 95"! ZJAh|P^%0DGnm/L~vU8|zGÂ_j>p꺫^ǎphΟn]횫>y*MnG$ϿSxG>r#F o_u^s$ ?}_|fMQ9X}.qmۨRoy|Zb]<ɷznGjw.lgw3'?qۭ5.]//|w~o}+Q{޻|?g> hӥo~_k_{ ͟}o=Cxk__j{w#}k%[g~<<߃^: '޷Wkw679O<#@M{lzgG[K>7u}ݴs/>㏢F3z5]|f 2ڛ j:jTLb/OTȝ:{S-drAk_{˦QBPO:5JLG;gzjȥ|p/:ՠ&ܱtvUw(Ꭱ-pn\tеkᄆ`"t /JWmF@GoWg:%:´Q]zg,Zt”4cFYkߨ\6cα~ZF_k>ϰ|pSf_{j._[Gj:: )Qըz [Siڶ0R\"T[VAʍ'(PֿaiBX(_^YAq?yS[RuV\)'Mi3Ji>ʝ_JޛЙm\nFA.`R>EĀFYK J\gٌ6#!vʛ ^Dz\e (3  Yb1٦QQFobB T:YI-j*[glZd1ô^ID '+ȡbIh˯U|P̥6G*Fz׊v2l$t#0M,)dV>+Uw6rfuskwk/WјL>3ƅ leഐϧ#LnmF1ɬ ׭B"_-ZT)RB,B5ʆSBIbec}G{;7J(T ەt0N=\lSg79U{+:UsiKHf0TE"|6grk\6Od4qօmw3力۹PIE8Τ2/r0JM& 1N#r%qF't4)dI6 4#'ΜdB!kp^Qd1Q \HH'9&rLE"p\PȄBτ31Γp*2B$lmEsx|.G %D"B |=1$Rʲ0Y1)#Ť.eKbHDL R!O*DžZ)@ ʓ+Ì ZĚgL- 1b)11%`%$Z{9" _*c\Ó(rR2?̕+" &m ٓ'dBHC8K(aĂQd#bRAT2@'-\*7,{Q_GF41eA2Y' ."D]_)6a#8#"p!9P,gTc irB>Dl&W"IH pT*p @$ȤD,D-.'dr d1EA'dž9[gQ}; F'$ TmPFؖ.?:l6۬cr2 :0'Ԍ:E˒VKzL*CE&\(eIu.cͣ7g,&["\6g( \+pMSu/eLAMKXKnݻW$CUkXG}.} syBo@qM3ܜ/Y틮HaWGhL阝EjǼj5gq9Oe՘cd7rVvCOk.3촻|)u1bdѱ8X Ȇ̠@0x}(pG=KD}4|H Dc~S8EB.=  WAJ G¡QoG+x2 -nA1n?%ӼϔS@>K߻n&u\vWL2ǒ`H 쾥%ϛ,#`,D z\Ͻ2^Eܸ8Hl5j]M-BnGCIM&>wt5-~.9] Ap?68 jxVL?EV3ɛ/ܬI7u,*]wfqG D P. MPm^q܋~˾ y<'eo3cͻ]ϵ`ÞY[aQޝ.,-Cnd2S1/B+օbнh=v0%1ƿ w(t s6¬;60:=nֈ8{tnhpAD֣*#_En|VCȍ7zϾIHct9VOkmB<=-/>=[ζiӧ[z]޾M1h!Zc@_'hho@ Q1~}pJkkft7nB m M-sN::0l[ZrSg=ϴuhrн]]#zwx:sr1MCݴ A[Z{a{tFNh;|ACkb[kk,Cs7#:;N:'@aϵ3hh$IojξpLpO{F GP.dZmeu 2an Auz{im=C}}=}nஹ,fduMtls{\zH!B2un,Z. VG1BDZ:hg_h@XM =]. b3Nkinli?wtLsK; v$A \.Iu?c5Iu; !Ce^ %Ar'FELxwss'. 5"(.AXQI >z:AcmmGPI= py%HOkٖSդP_`V˄#R9>:I;ׄƸ*WcB. L-B<F&$.%#B) G"&[c2q` 'Q0B(pLw" "d@f@)q &9"ڪBHɗHG!*\($L$@{8K܏11P"#Sx>4,&R  =)LD,pBd ŜTg5:۴I$8">D\XB6ŪT52Pf5@MXJ bJL$c)= RUqDIu?4;8!+FFbIN(FVDeLԠ{!@(*FD\pb/T4J xUI*.! tNKe9R0$MŔ͠MJՆՓf%Ai\-#4RS@,#NLKz-U*R)qX8A8!IfAKD*3^EBN X4!#@>&% WF+ZOH1JĄJfސ@q\2hOOr''F{W*Sȃ3Ї@o`|M\mRLaXD-h B>&&AS&,F,q$|BIzpI>& H#IRD*yyf!#b̄|H$ǘ<3&!MĆF $]B\8+@*<I-!S#!6wlR 9.4hjq0~J}!}CTCu[(SfhTG=@_n:H((D喃r=K8P^K}p~Ku N ]>T')R;SuǎpPQL:T}=׫O^uv8^<{#Cuלa\=J^+Y;T{s'ؑ#G ]{I C`*^l=z|7^=zQ{!^'^u䟮M,B[O9Hxgvm탟578An}^OOw|x >Z[|[n^ rQ?dnkD#"T}Zw]@7_Spf㶏}N^DM[j{ܧ?q+ !ⷶ=_nG\O( Qٚ}D`|| !B|vk~>"Tw=_c;X_bo{_A>~0!j~k:.{zo}~@s?zõh́0oo WHWgC_ WݗL>Bx=ߩ H@~Oo9ƿҺ%C;wz?ʺ,Ça,4uh/ KHC=9݆^9\G@QW?\K 7y3?Rԇ$Gl4Fz;>?Jy_/Ou?8rF&d5<n?M⑻Wn_g҇x?zGDƟn=W/,H'@#ԣo!}O=$< 07o*)obqt2Dg.Ϯ'#_":߹1 Z kF1s5W]u='jhbC݁AK@ S`7꾺>)vU'K3a7m!vG+sVuZӯ#ǂH"J/֕ɋV_g?$ͅq+Z>Y׾O&+L@dx{z x RY 녅mźh)4} O~ o˯\2WUÑhuPkthX v/O={̏sTX,S;JyTz{pP j֥o_ۭK'{Ýwb$ zv)AoR6 NɳfymN:989==9=o{\>9t|p 'A-woV{O4.?9?4Z=szzҧAv&]ݩtr:ISO}t瞻<_z}ĭRP*UZ헫|qxj vwOvN>?:=|rx|p|z$eF-j>wkNo)Px`opeuOjVw~?̠pztstyi6v~jE.uzý~[w LGBsTyS'@/O?sZmKjܬ\apX5O'|OOp3ӣV٧"V)[IRZ%˵gw/_zgN:KOaoENv{j%n&ӭf)8:]w=흃ghO  wv< e_!r}}Kv<+Scv,t&As RN 2 GnhǯEB6Ov|XH 1D O"nl2q]C=ypxl:h/b./eJ cuN&?\)k:@2~A~bӰ7Cfpx<"t&g.Ҹ:!;,>p)\dyDP($_.Y߀wPr8!gȓ9tr8l6ͤQy0!,uQ;@LWQ Y P[!39 '`ecJ[z#Dc6uϠlBgY+k P&sKDc7ӑC&6 G!`U)X dj" 9DJ%.LIX"Rx"&٤QTDXBI$y"2a<iim.ScVϠ1{K4w.$J6 dz Cm]֠u%ݭ5{J j:){j_V JY@,єCil˛vKt$.6eX7˕R^P i8`52 3mջP(_`;^NAp˺46-l2[Bak ݽZ,WmTjT@)d'sh,gx0"^U\V*'N-w> YG6I3d:SR.Z#4Ѫ7kTmvL:$xE}Ldzәb>Whr:6[vErNV@1Fw86ÁL%bjM0B2nuT30X'&kEƕNXFIЪJ% |.c‰|f{;F//FVVpijh?x~dlxxbb]2v,i$vV,,⧦D5E Bst۾tA,΂eEρCE/^f.\Nc.nut;A۠~AO`.~M<E ϲ60Aֱ87>KOYXI,x/'&/Chv{NH`AP! agBИI40fn561GaЈ&p9lfvC Hz? )I)4C"ט%BV`.7/#j݆TMc,E 9|2ˢt^jM.uYq@-a\ « F2B}<`2X<[[fJ۲8`+,#SHHX`\R.]mnhzlF m[ZPzrY^<>SUl"9v]"F2&s)L6 ^[Mm"h2h1; pfu`87@ơsim58y$M.OvnGca -Q)'AƁ`#B~q=7!@\u&݀f?ߋoeoz5W$'e}W~@^AO;C+k]p֡+xʇ+=|/7}v,p6򕇮|ʯDX[ыOPF+fæEH$Nf3YdA]Mux@LȍzF_+JBL*^ v2ؽwW ̪T[jZ i ƬNgP.#^Mxk \Ј8 Q'`VbK+ 4jInD%=koD~݈om4F5jPd<6_1ѠվoC<4"FLJGe1Y$,wTUI׋8$dBQi4|xbjMUKNۮR_uufZ52UjЩd-=I׊ju5|@mIJRޤ~Tw;݃ݣϟ}1M:ިʢix2SMսn` :٤˕j\kz\*yЃ? sTFPb0bYk|ר֛|ϊ[N# Ų|L-S˵|wrww\٨u8tBdr"/Vrxwқ V+( [jN+RF]mԐ_*|)%R/Ê??(H&Nz2\ǫ{GixFњY@WMM^wp~N$MgP Z'D.[(5vUj؝ 9*AV .G˷tzI͵vvr O+TD7[lZN(f*'Na~T,W:`* RK\VHƓf9+AOkjXV`43Lk|8VIdlҾx=ScS&d0T&Ө-vwkJ>]wSٖCMIRlK:A&6k$<0Umꍝz` 6E,GTZJg*6FUηf+zYfARqJr*rG)L\Ѷ Ϡʤ\!D~{t|LW֪-Tc`:amn72T7kb0/** ItjVʓ)֪lTLeө9{2D$Mv㎤ˍNVL'<`j Ak聯b-0ۥZ=دsL F_&Serč/'b4^#tv V"dɌ@HzJ\P)/XTBQ&P Jyh6=E4Z?0P;L:`zɮX> ϗ m[k{; jRqEA*RFH,ێZRi4vZ\B),-[29p<*xP˥K.wm)cQ*x`jQnmfV'K~hCJ_Sl@".(^5"|]k OgҊBXmkһ`uLSLB&rx 3QI|vWH8Նȗ[Z!G&2j'V,3M@R!d4ȁt(:h#::h5{{-H>6:B"WH" kwѠT9@ " ߑ!T )\3({X,mNpjK\d7׉te,6mcfhГb0g,HtdqyL2A#ilA,$ f/V)lDȆ2K 8*Abl:MjO$VL:T7RT3&D5PA&7dno 63 Hl!@eШ +te@UӸeun+)+sxp4>Hb: u!䷉K>~;dT`dLe$b[B89&JSx A&W >a%gtk$¡D!9bH@V]DeSi|&Jpx@G9Tu>fshK'1X< V)Dt&Ϣmzhltxf-p9,6Q!_usX(˥;q6A`J mi+$]P̳B6EfN爀,,V,`3:cP4l Db ';qHu5dLY`H HI4&/ Zh,&e]" x1yB&Nc yɝL$r 0-]_BD§Q"Ɨq集Bk0_ Jo\6y.QJ4:5yK0+shu&Ig TvuXiϼ E@2iuƢ#)Di˲5"b%>C'3db6G)T vl۠mV 4m bd(u [:¡ e>RgRa"c3Bx0#!p`8bxTd6@d r?VLm;d.f7a5P!*-QAqL"C&-67[6ͤX>G(vx-$ ɦ & BnVku;__yL*i}i ւ$L%YL:m6drb!1ydZ&HE26x,t,e[Tz/Pt&E':N\@P KrōNP|> ?qA*Pm/s;V9H@ר,6ŗl.A$olBE2Poߎo +4*uH1 0V|n& -nq>Y9PeLJCc|\p;LnXvǃsxBX*d,h\&DX? @0Df$FdD2Rġw0Gz0Rd>5K(-W4]r<8J1r. <$g Q\! p qE_!Fa0k+kb0-W&l60dD&66\]! EA_olVAgRi527Xx*↷\FM v/myb%P!`<>T #D8 3~ EX$pEt,IZXb }qúI>$0Au$B/PXbI P6h"6]0Ё1KlN(T"2 \C`2- ~yceqy,1BˠBajp{~ ]t̅Ǧl.RP2xL>'r2G#m",$FH( iKFbSXEf!sEsxp>2yX\* K"dFar)k[>?e4 V~p d is\n\NsP8k `29lu"K*+S9y) &|$"DEB'NW Lf@`fPؙ'^%bg0^ӃM6xP.R N&R)2H߻~ٱ)~;zPPx :X*Cd 2|6iI\ &'1+BǢ$ ; ^/lȆA0=6`|6:^-C 0) ; O0eɕI´-]_5o~Ϋ!nqf||hCack\ΣnxL=񹻯nsإ '{r7>BM/)eTE>!FO-.NbG'34zM0'ƾ[ `0#("=vЂ/}:v1z5Z'p WXr=|xx|c8z5X uo1AcQӨIjvvEh%^#cyÏ-g3ѥ nsyjvI'*JPcr? _ O= *pqfcqa$~)R(<ÐqMa&fg/<:4 0!ר9O((@nvnfr8裨řIŹM.3X2?uyn c|lra&sBP gtxT~13eSTP1NCk+x$K++Sh,XU!mD)p|xnřቅi&1l"D$#3 󸩑P I%28d:@"Q{1jae/B}ⱉ BMD7֩<@A ZfF I znb %2 XfPf&'؉i=<-Ih|R׫52&'Ro@FG&&qssC؉!DM*A`p(? &A\c?&g1DL%n@ؘ +ɉ 4șɡѡB. /-vCNp"gcCg'CgB?>Y ƓeI#2Z%×HDf77^][\č0%rNZ[<: SX/PCCX<~ ]Y_sZShIA0%T B _xrb7^X/@{p`@eotGGALjC B=q_+<zP8RP(j?336cfVCcS`ql.RV# |==q y D7+!&#@D_|&z7~G^:^zǁ/+~Mj!{ϲ^cg쪿\kWڸ#z+}٭w|Ȯ>_"!{^ooeȮ>+xM^/{e-9-m|Ȯ˥5џWե^ʴFvjZ~s}^_^W{LKN Wե̾^JI# /^mMWedz+Mٍ5/dK/d/!x6 d7Wd]ykW2 d'8;J%s(zLm:Foowثh?Jo;~=~v`zk^kv7 rGC"G|kurV:v*"nBovOlxSUvnk֫ZgpWo? ~d/T!~pgXS-wNlZrTϔA߿8g 'V-JeV^uTA܀Ν|p4بwRo;jX-5G܊ӻH`Hy 8핲jשw\mVj/esG`"C|-+jnw4 D:9i |qk˙zNW{&i; jz*g ^1DDרKhZ\ajݫeS|V-;F Az¹ R78ۭ \u.SPtOM&ok|TwwlYtw{|)s$Mk\ZnJFUVn%+w뫹\,3|vt4Vj BrhN-˶J3VkX"ގwF^YZ\t0=C}|v9J4UW{ZSϦӣK';GJ'JlJo i6R@+JhWj{xfRgVu#| ^]@v-jh>kz3?p3LpWv`\4_tNw//#A靳@~4ޮG;js`wo ^ @X3m}[{{{;fhnkݭ>PbOǎZr)w;j\GPG+/8cKRWdt7o][;wnUZb,oXN 9hyQk@V\(V+~k^D~tQ" `aZ{;H *u[d ?'VClƁnw[M GF>Ո[@?Tծuo4Xy ?YTۃ6N{Wq?"t9@y18 p N%!(W/>wn?-*k/ Jnz_ˈ盵F/&E_w jxv0rs[B%K搹7w4'6Nn6s+?u ]F"Vv˅R$/ v=)3vdf9= q;WFij@4MŲz7#B\< |T?o uzE˵#P҅jek=Wprk?kŽ^ɖ.wV\/D5A8*{#v&Rcz v緝🿾l|g0 Rmu%Ơ7H4A ʵ+MkpTWٷ"pV`+6}J/|ϵGX!9G| bQ6ڝ^9Wz7xpbƝ f S:-3rnTMՈO"^2J9$'~1M*u+hd[.zၷ\393 m܉7Sl:8!9'g)G(W,B[k_ȭ a#ytޟ@/SS3O/섇缞h:{^WT<!8d>9<a ɨߟҙL #/j>8>7\.@*~~ӃݓOFHcԷBIwr?1c[P8޶0|K$A!CAsE+QH|?K{p$¡hwAs/w{YbZh8G¡xvD:#?T̿~/ RarS aۗDِc; 8,H Hd.U TO[) !Z֌4$CHʣ۲!gPQѹmrгm%O!Ε '61pzH&[F' =#lzV~H,洹H;2b\n#Kj;!&HnFH(k̞*))H-g2a+̥xh$@o8P}ހEc~݇ `h:v:mV#mQ6m0r y oKo73|줼fl8g܄O ݡTF}&z,zC`CoCڨ7Yaawl;Tx|  JK3Gx*`RIF?XMנB@Nn6;mf;>?q7.'L>HUһ!y+N"|!ݾ |l:B3P<́!MYA7q;:L/!tر-}?_Fwmr;6CZ;mmRnHkLb>D4 Y+5-f=͹ PMē>cgqOcX(h ~zft$[)ݭSt:fkO@4՚NUFGt>+s8b_1{$@|pyt0WФ%dR`YIV\~@ ,|$X wu陞ٝY4 _/o[rԩ{nöx:ak.O\F<`]NJ5L.٠zn?KsxD['&j6tިKc1_Fx Z=Q9g`}v01cr=T>pڬW _4FA;\^^1=zFê~@7,Nh5ß}4ͧß7p~=#XMYwA&̜*ygcFI??{a8sN 1D7z|N<ʗ8OHX䟰ǥs8 zG]Ɍ?&cឆY\#d=#`-ϜcIJ^kшlNO^w#ɥv_yꁈ?lvDL7",V8 nx=xZ쟸VhfK/T\?*`QR:!|ej GWXƯ?p䟱v|ɡ>uOÂ)S(Ӈhb>S*0)>䟾g''R:W>7'e,^?),EbNN2)_"g۹t6/?{~~͑ ?^g/;|sRلD2i4:_:/F9y&z=Jpl\^/i#ۅ}uos럘I!6642 O*#QFY'Rw1i}8$J1XLl\SC&MdsLѬ\.GGF3BXz@H'e Ui\u+RtxZ$VXL,Wo"S Flc,> 9 gB DbrYt(#p>GlD:N=>D d ش!;zsӡF%Sw@z=O]\DcSHlLJ cT|fvfJ,\dsD$y`mC bNY9 ڏQәS@3& $^ιBP4>-LTdbrqQ>ssb wZPR!3=Pފ}Xk|j\be2Xpt;DFF9nR.2x  J}nbkL4cdJ)W h!<=?w~1O RF3SJr5:<6::Bo罟m3x]p$I/QGw$"p,`qx{s }sKy#v"1ellN>w9Oћg@L0>6=)reǢrl\(x4; >1%c;ٲ.Dy =sH\9ǘ"D=rſB9|`DLa=8Oo=ґi%a 1d?~=L(}}y9'2l8x;/:g1챡\}W1)4'-J ΓpziXLHE,>x$9q̔H<ɴ`daw߹&IRl9Nv0& C>k=>;!aPBMt[p(ϹLWxpwMd.a-%u!fyQ] =v&)B刅Bk!ʜ'D 4=>9ٴ>Dz1ͽD(~L.G,1!H>GLR>L,ހ }lNeJ|I!cS=:@1I> 瘧FRt$55%aR8!O6{(o/Pm2Ld@>r$w_p$rτ?=<JpRcq2x/nDb'z[iBlB&س5'>1!Γ{fa$.LM~b[*qÞu=46Ox,b'âitr僯zGDZH;=OBU)b SO:Ɩ El>|3LebgC7;F?vuH=}FY.9P(yXn6/Z޼t/?yz(qC?z=z~p{yZb>yUUʟB>x*ܿ_iFJ@:sܲ<ptbDل˗Nl9gr"7Jv h7}t$!J\#LFAh8S(bQMT6 gY- |&#xX#r&hזKԒ|dQnT L&DK|)oJ*AuPEo(E,STX*[.+˭,~Kաp2U(cz h%p"KK):#[3lVSb* kYNͽ?0ZD6. JClŐ~^0 OGrr$%֛vHW\:d:RWISP$˥]QXD#`h_5WV= ;ILF|K"-DQM|F,Jg|ژ܂rr=EY*6*dȤ#T"5G|Xcc8 6(dCh,؆{>o(_±l)yde\|P(D&*+"4<<ު39G=H٩'? ́fP+֢hlq~(|tXe˲B,FݡRVnUL\m)POg/nFp_X( RV4!׏{" xS_֛+$BW0E>ԪD_}EVXjZ)L>Mh"WMCB4 0UA)[kK˵o'a<̠ P: +A?.V|ev}Vw,ţ;qq^`*b>ə,Kb8Б}[N jd:fekVDl&,{~x/,%XOX"L32S>{,O͙` ӹL!oObz5(@tH=Z5KEXu jOԒ\l,/3h1ߕ*H Z̧s<νQea%Kc w⛪H,]*@{H"[k#h9O.wb2xtx8ٻ}&nDc !O<7'2XPNiVpeu])IUJd16k`?ەz^'@CFR*+N| R9v;"b"XMr/hU!KVJЉ' vD՞oID [t4 GK@򭓧"KǼahF.UlES*x\ '};YNe:&f)_St6Uy+o1NPΧ:|0(e+$TXsS /lyZb*|!7ղl1l\ZVI_Ac^2_t\NR+R} (DL:I!g(UL-m%JHUi3|ʷiտ-33 dYHb:0D58|ko-dqeZZ*MKe{HEWgx
h*fIϬ\npTy׀G3ɠW?DސcYvfj{_Kf[Ͱp|>|_ I#oM}ߩW"aת5x"jt:ՄlJܤxBbr>$MvňQ^qfV¶`4Z RqÏ 7Y|!q'P$=x.ަuIHFNiuΨM.ݠuڀ_|Hl97hgjain7dY]G`-jJhd*MXkqRǗ/wȢZ:o(!;26<26fuF:ɬ2ZLŢQ/_zsfX*:|v- byUh'vѤP3:;n]6+9C8jE߶ plDx=Ҙykz7df5 j&NBx`E0X>k ڍViQS {x Y61N{ ]Юޛu`U羇粐ϐ0ìh>7y7ಘ_";~cGװ81 Yu>4zQwwȎG4h2`sf]yiRt4/6#mf6 S5Vi7_Z(u>hgZ݌DU>G#;d$#;:'&n[;PyPeS|KYJMH7ͩ d1cdr<6#>vJ< )Rk[#8UzWeu9-zrA?F>Z#6#uyLV;8Xvf̂l7^iMNV?pieύO)5fՄxl`sn֩[4݃^mIBm6M ֬5#Ǩ2mEp06Uo0;C^ n p)_8 uD8pg\kk|œ2jt6%m ;`]Nff~Y..jtHi].rVauf Od\!vtkjPQm{z;xzQ>m&͎߼ua L&ᲂO/jnƨ։L鴀V[^4Z(zvVN&c.@6o ՋzK9f qBpFq8\nGjawpˋO[^pNjmNO=|o<`90cѐS3j ff_W1w|xС5@xܨzDc:Y`Xւ'fBa,6]E-7'ϓMz35=a;ai6vꌦBnA厇ް=T LF 6YP,{(0Yϩ*G3Z^ ,>GZeu)DzEnQJL.Sx;LB&92$I=:B M8B11HILpJ2BK|.-7#7@g͌o߾)Źgod\3?7#3qC.L8#3輟l9⢇H ![8. &X 7HƥRB'<.ΕI!7~#tǧl{fP  Y>A,],$RلP$=!?{>ƕ F uW!OI];)#4.^xؙ7N*gE4cZđpX29*Jt9^+Cl,Fe 2 a> R#!6;(91:AOD Y ~ hSTÕr)46+QbE1ԁ!#6.iCl6N(tMYbH1`(!711ɡcс1ePFƨt.eyz—Ky\R)iK1xT:_ě{'GIT2B@J>g:[2E:z8 1D `y<022T._MOb) o`xX83pŤCt:i`' r˳v{byR.OBݶ# Q Ig 3ǭLB#QS$g~oWL hlN';3Y La2G<'tC'L.XTqFt>m髑yt|Gg챁c 8+Bmݍ֧Ρ\^/cK##d̄K6Hљc4.T ?=&f=w.HУB'P|dBʺcc#(z13$f`T,J㲩gqz̖I$b=Hأ;vR($(SISg&'eS&[,p9 l\D1B~w8r)1G<9*S ELoG|,=|} &8xH bƧCú)` S(t&x9wp2X xB>| ϰBj Ά</~\J $LDo<0dzXۇAqI⢵b.p]H>a"8?6{H4?\OxN2FhxrB>.FLBE4IBciwrAH]I1=+)fB>Y #E&RG"9cRĠ%bHǴK9#c4Cض“$H>MaxlptD!8p8>%p÷#ǫv1 ^6r)eӧl_8W=J8,,Wp$q'/ d H6.$b>n6)Gt$JJuvߝטSH|Aݨ)?o.*_^=h㫛lRkys-G/H{LE gW5s7gi_SbX_¤R[uxӨk." xonv6pg|a} {᝿D^ߊ=~<XS! ,{W /J1,$"߶O=x]<[ y7?|}Mˀv2e#uPKO~WE.}d\5!7D_$Pu~6npXNt^T u^ ׾ }rWzN<6FޭB-&Mǝͫ^6i&>?{=;V7ߒA+Q/˽i#=pߟ}<<'%BHz*=޻x={C=AͽbRP{]~w'N?6O|K(< Gíޭ~=?3 'lr[ooƛoÝwlġ7Ou7^ҿ.__}7rAC҇GnөL[ z{Q[޳i~uO RV}hQ[V1w0̗bT, Wz.W:ͥjZjN;B"nd|vܮ+ս{W+K=W_Z_Yv@o ;Ed2@'"ťeHޯϥ=.5!HTYC|0\n+jZwr1U].y~6ܰZlD%)Ԉx(,/7V{v^:jsSvWī+5DjRեVsi텗ݍv\۽W^lPVj.J^k6R61juRnUZ.gh/UFڽ{crgYW{y_n4t:ɵWtP=ntkRkN⃢cn;^ IVU#:FݨUZwecwcecϋ7T]u[.DN^[m7*VS'JVF_0Ζ*KJTR{ _S' qEUYrmi6ZeP2XH6._hB\|myhݕVsPׇmo˫j {kYnիvӅlW-?co%~Cc#Fgz^kWZ}O[+KA;b"שksVL.@m;;Zin2Az=X.ZY]i Z -,A_+ݞp8:PrVk-]^WݵRwu~I/etwKK[zލ{Wۭ3Axdj6'*vk]~qo]6|$K+z]k2Qo3P4PlJL\'W E nA]۽\XD"J1DJkYaCO--o^`whNX: |XYi^_]Yi-lSk6.J `tR(^jf*j7Z6*V*|\$l(J^|iuZ,w]i6^lf3KNLkҥJ"jw:/Y]Y[kVqr٪M 6䌵0Wtkťڞnqr@0JݭvjV*04^zj vjr0JֺJ{]_*۝ZR'-?a*ɤDۮ6`otwcI`-Snk]5`3 fzoV+[r^ʦ2J.@(Y]aW_aI͟f"l6 &0uMhT0o"ٸ?$BQbt<w:_qd4eAr$ tM2hD:0E"tʪ~qH߀dybJGKB6W!R샽|Yx "nkH堫V1LJ5=WE\0ջZT0MjʥQh&M[à7&L( (~*OiJ NX FӘ,hxbxz)&b3;\ dZ&B2S,Qh,h[4xK,9PU3P aU;ْR 3wFX^)'\s¥PRD,/#)l:[Lr)%h p|)_eө\%SAJ.hURa_0+H投Xj")/8A1ꜵz qO8 }GJ}l&Dh\.Â*dh 'c|T.#j[24XeV(8-d4Tr%\AAnq^k/j`0Sa-K&w~jk8T%0fBR@0gPd8EގJyz'|>/[&Reth M Xt2-/]x}olMRbV-&`> 9t1sx"1> *V 'QLS,]c."X42&Y>=4ۃ~w( R`92N L< YlvR HBh$Y@,6r.Lbp$F3 QȤT1R x \'L6G¡4,Up[.rVhZ*-t@!fЇcd1ƒj/*,G^S)f1TǗ(%JnTLyt{\.ߓ0oVa9HpN˛ټH4р?HTFFY4ՙ/SAK`"1`2*C fo2b*T9% SiQit9nnwjg5&͜贪f!O{'JbVhvvn7z{FpoE9)G 220Hf򨃃JAS*5rQո^q.I3QKL0.4zqaNW,f_ثEo߱IA@6zѤ Uvxxڠ] *NRՆEѪ55F JA]zB ѩ&hjF;oWI7@FWuzF!3qѭ|A[Yy[tfѨحz0Up*Rdb>YlZѬU& 2խUkPQyq7IO7Z&F(_uhFg0F6ŬS*Ѻuvf~N֏<]r8:hkf`WNNm2J#wBǍb<Τ0o7j,2i@ǸqGi Jd?Ѓ:Lv~~Q4r?2@gW,.ha8[vjyZ\pC#wQrdVΫ`
JL#8&: Wlbq+_gSi<լ%Rm1nFq4Ƥ{Z9:GPLf3[R!M"f2DZtLVAeU`0)J_O*6 fowXF1hZ&ZjլW-tZp)zXQW/S"T3;Հ/}v | 9Ղ O^.S*&!+U|jNo}Wg$l ÈVF?fqn^{b#!`%ǬFfyӤ@Ut׀2Z\ЏjLз~ X<*MZ[ZZcv{n Tw!b<:0?;7?Vif љbV3c[yټ~n) /S`6‚F ɤ]T<~7~Ekj*BќQ1jh|Ҭkdu覔:ڗluVݧ~cvR3Y :韟S/́MybȠѨfUZhaQ1*4˙q>秧faCtvb_\8*$>SCpJ!pyDГq!nuQIŜިu^$y[Onkēu6z1N4:ha4:٤xvlRmT,vdj>9OtZ-o:_: <ÉNfƾs:8 ;ܠ7whPN0JNhvVE 7-)J5 Z{IΪY:iV9PY\E&J@sF'ЍfGϼ2=ɠ\NB4:6݁zzXIT3xޱJ6jlp|% L2&\£`HT'x  cL﮻m߆9oLd>D1Dm&]B/e_xLBB!Nѷ7?|j)mP/bSޗ.Ld_RP?T{|^Kȃ?ы.}Q.3k룘B]~BT@;~z?}\9]R]PEaZ,O`߉ l븲(wr{/r/˶ܻ-K(^DW; zpOաb[JVƫ?KGyeqgSա7^ƾ>p8\=?YV ~.uW7p53\))\#uW<?v=OHO=Zkw^9053\ LP~>P/ek|^G[%`c/LcMx}?߷j# xn&wd?%jcV{P\yYM,ta>5M)ƥɱ1D*M <ǩu'HxJ0K\&O&$b7!KxW薍OL)R-ldBO--$ YȀ߸BQs뙭@q-yt:W*IYLWPԭ?,deScBP, 'Qw>bR>&qLP6r:y=ڋ+M*<,z7f%RkTl e\+'܄z CޅzuQu2Ճz55%$yÛQ7P+YT$y|0DԄY>3Fx_d<@2)b2PĝXXHC539%i fg94*Sħ<47 4DNfRmrDIp @D MMD aB|L,qc26b"vH&/'69ژc %d >KT0 J-2H4I: y ,<.|Dny1NhcxËU! 2&fL\rK\0J"=TvƂڅ"\Hl6nHǖ%?g3ɕSd&EmE. ԸdLD'1bN"~[ zH9FҙD>G85!ɦ'$RX& g, dS G!hjL$`qPKyο8'Jo±.A~dÚfb()\̗$"da1 7HfX4q2DI 倮''l t.)s=N'TLx҉`(DHa$cˣ1$"ß.,5xؗH,P,@ 23q . xl.<$ξL|!hF(Ϡ6 ǒĀ4"Wtba9 ldI K8!A|RJ0`/l>g3Hn>ij'g&fǨ ZBp;Cۦ2h*㉐/ >xb)" . 8JRYp6 Q7D"ѐGmD}?K_0N3@d,Av,x]5xut6~N̈et4D"RF:5F%M^ݟMH 8961) e|\8*91.1ŋd`̱[̅fSQu,)P2Lf2)<@ J@dӹX8KcY?$Ag.$PPZd5R7"''E\L8GY:]V4~ @ eғ2 ęQ`rnrjmd!6T5<7֩3ŌeBʎ!R*oTxqٝT6QTj|;8;"0CC}C8,&Q5f FIo֌Q D'hD(J"p8բ:Z7ٙ9=,!ҁ~i0D“l.՘h(g4vJC}eSp$ԸѮ`{bLƹYNۍVcX:O0D6K )(68PzJk;Z |W'F%i\`,Ę- LVnPѢ҆ɧQ OⲸv>-ɸ}N`wznM;!(" ݽd.MBZCjuNq{nn4Y!3 *S(cbfq8g{(2-h<[A<5JB'!a,Fb>FxpFjJmvA l3L踸 L~nrh<TYvK`ub0# ndh`"1}nìVj)t|<`sAsX:a BG?}1^һZ  K&>< C尩q;J_uqlyg oL Q0Q EiT&& $aIox]e&ɣ8JlhP篊qǦY:֠߸d GSC!QrNhݨrZ5+jen=`#q j0^7+רYXaw - aP(.LRni3Oɦ6㞛{\&qI1$!¤:2JZ, L1175V[oj^'(r&bFtpE4Z.aU*a4峮lm. B??FciT*օcЅ| v^^[!zo/sP@@Йl-*[k*V{{^[Z*\֊#C᪺ OQ窺ށګDKKsmGg'ףP@n*CͪcG[;ꦦچf઱ep}mum⫪ꪪ PꪆƚA uM'ZڻO56464&8"v16ǠR`F:};Z:+k~횶=b)kg1.g`{Ix[AojkEd3:gQG *MqTp;uͭ-'߻Owsl 8Mp9t@kh{`yK{SKoWwg7N0|BK9`D<?OЃۛjm8`95L̾A!Qx|$AT64t(ve+w @X2D2`YϹ6[:j˛:;tI 8xj͠yB) ~ h{Cu[zZۇYb1. wt RBi`cKukOWnM`gOwsKQ& >#q ;I1C 4xe[ζ]m͍uMm͕Gۈ-]=$.Xx/*=7݌*C}撵6lڲ}׉Mm#~P}vůϝ=4n>ݴuǥ+_} T*Nc$K_P$K$SԖ|$^xH' k47BR^gG4F0m$CǶCQw\Qq[M`nA GБPōŏ\@*Wh @g{|@,\-/M)eQh\8\hP\gkbYZُ2B]B;^Q%ّ=;BdpSLl߿)oC3Ox]C8 [ SFm>o2^|}#ϼ}DG$QiNi5\ȝ_{mw?o u;LH6!U)UJ@`6kn{@ZN,H:>)O `f>,UfW_w=/u{KD66>>6&\Dz.[v\ }h/vhma,`saF*r?9[+[O cထd6:uOԁ8X^Yq Gz<h`l.Ya-v\v5]O<«oHMCԎ;ڡ lݕuߺ+}O=[j V `jS%(E%cM /utkoxhxa|Pyk(? F.s}U,~Q|J1;N{*PÑ#G+*=rKyO-,/s jI;HE;okPV)6ko/~QvXe"8_5jgR8_x2p\]KpwSqoqg~.]ה]8 .ח_wt]X%׭&Y-]55+t [sIokn+_"t}1o>5(5W,}|Ň\L%o _"u M*uW &S0=gII?]$zmG.JB2xU(PyU| wz&'-u5lt!tp{V^ 7ZvI'W_X||x=HSa۰dBPRuxE&mS0}95H([Xʃe#H.LR}Z"8ppzlf  &3$<>S*+Igx}MΘc+Nl0OfLҋemU\_\ڔ g~@fye>O!U szEc/pu&)횀8~ lּܧ%Lnj/ ٽ3`KvC~e2D2鰯 B6@;ݛAtZy.!!fԕcʩniq|"YH|v=erH"b4\&n$EQ71$x%Hi @ts^jb p=ҙ7Sq;/*SC< E$WDlAËx7ٍ g*w:dzkciF[<)1:H-"YϩD d6ZLl饥e!]`t$ clyE#?OFR+1O/x݁H=b2Db˙ fW3IfcBQY\[L|&P3IFKK$(fi$!o{ṇ1$@5 ?]ogҙX6YX#0 3!W: v_kqh>AldO1xz H2rNǼxnB"'Z\~w`׸gP(KyAkg@/bݑZ~}*OAÃ21WMōVHcO.ozdY|Vo2`;6@SUK!yЪ/;;mf3!W<3#Nhh*]Vi&BgQ6oSX7̨ <(Mj+hcN[ 1_şٙc Pp[5ǯ,o jM^du]{7LZ02HFx駖H"b3٘d!?miϭ\а@6APtvI`NfRc5UjVƤQMv]X}bf4}׽k0Z5\="]%Łg*&Y-\ݠPJh3y4+)Mk_xnFe4; JjѻtH⇧|FƤE ѬIsLߟ>1fٍY;NTÔ*bԫTQ &0ViڍsS3f%Q{zv F͊I|wcج3z^Wʩ6CѡݜV;Z xNWoԌ$~+lVg3@Ce٣QlyOC'w9ա\jvtFxδj r!4ZfcRj՚k7 mZM!p9lZq| 3~I G<@cwga-o3;hu[Mz3fy+Ei*HLZ],V[.`G֗H]jk4F;x iêG?5pkM3h  Fd{S[Vbjm}ϩa7 _&Jcryv[^1X\~r|{KVYzw'|/q92707_ lH>2KȥCD&dw9tdqOEvH/SyS"F'[v@iCƦ8=L6O<.`{zF4^v]iT~&k%v5BJ (ίg^|RyГC"Pw@e1h,D㻏s̉Y$ @d+$Q|!6uRHwH|>" AffR6Xu nqt0c> i=61!Õ X@4q,CQ>^ YҷqL:'hq)|=&.2CDS|^Y4г$2K%4F]ůh`3\6 tof24 09q(ܧ`!Wςi*, ~r-p rW$ w&`G{5Krًd@H8<7IS+pqPov8EgtB' m zjղHBc:C:V.`kP[AAGJ7TARWqPڪc6p|ià|XWs(X;pڥrޓOID0Mc+{qq~ч0,,9;V][ցJ۟+<}{RK`4EK:ZuRџ?SO{vGjWϦ_~}n A)K^ 5GK_|@=w7^:\)jr_`wWw𞫯1o0iܥK~yR}m\_}WUs =ƀW}1Vޅ_'7\w5|.K.uXyhze{A*];w] *T"XyaJ.=]vؾm" xvUL}ڵs۶nD8 ^w} :P_n횵bcxNhʵ\uW8߉yz 6YY3Xwbq*H2ojyi/=.K>^9 kA2ůXwg>+WQgvt5W߻-zn߶@٦~ߧ]Ϻ>98~_Յ_؆Vl76:ϿcQ-e |7߼3p2zGwdЗ:m[*-9 4~9UqW_u%5A; %mFI1_x/}Qq0)y>}Z΁*/x:͙>N \n ]}r\ϬߔZ>+ HN?]XȅB-K2Ƞ,^wbQ/Xc;f'$TɆz $3NI%C\*%CLgl,_\Bc<6T\ I"`:%Hdjiy)>&A$#ao-e-ְ#N?OXD'bbϤR_z%xT:8̆D>Og|$bK2 R$BLo3-"Ngyx8Mˆ^\>^/A7)E|H$."ٕDh2uĤI:X,v92R)g,NDL6M"6񏬃>VWbMƐ|.sG مtABbS pf1ǣP2 !~;KƁ3K ~OX~6S^Nx$ԙl SW|a]?pe?{"!Oܛ\\HAot>=A$\߿mߋҹ q/fa* {bi>[XJ"$Rmk^Ir&3hhnia9c>PHljeɓ^g4=/V,3|d,YLK B\:D6 &@Ig +˩p, FOe\pgr|KmFgŅT'׼SJ*cL.Mԩr|>&F9J{7=Xa/3BgăD$MS >_t6>`E#%r?( |~V2z`9sSp8H H,MŢׅDl6E@f^ڂZs,S,AK%B"xUjy!bX6rCnx./,N:_<$d2B|dz!OH,D0$|"S"|:e>W0gb@bѠ/߆ڌ[SqX4M&iۇЇhv-xl0wgmO'Rau7 |ԛ 'bܛHѥ{SH$ ">峙dAHFA Zd(`j\q[gu{6G,C 5;Jg$S/Rȝù˵ٽl KB[rgꗖ d4`򠽗2vW:sRL/`je>GmhEBoUχ>ɓˤ#= )e"~x=w#%}l$E |m i]XYZ}K)Lȗ_JҭDw GxccCtd>daZc>dye<'Bvi) ?]\3c(  hs9 :>tX4F-P"^>eu;mvupH1zbvzJm(w8 qfB u|@>&7ݯ^\jwM=%ν%q-s3:l4ju9UoIKkԪWC4$M:Pja5 6x$7C9VͣXfC?Fq@\7kumq̠7䜾;@tz9#u:x$L& ;|0{fxE:{$pt6QS=($1wH%Wmê;M_Z! oMKT'VLӪZc\ |ÇXdJiZpf=˭)÷բU[@YBm0k2Y:J2C}o(&z-TPI w6:6ͤbH;=9-f6옘u5z GQphEй ̫KӪ$5KpM&zfrh7,fJS4 2*ˠt/ë&|¨j3eV:TjA 4<;5:On46׮WwmJvaHJsLffZQ}^'vceiބpӜv\3}gpٽ>^cE3vMp0,{ٍf9 z'<;`QljPw.]þH @epS.\j< g>íֆc bn& .Wћ QVR 8RqB~K]f7)~F}$oVAQjL۪s&i4{=IG+ẀWTPL}ԬӪLpٕ9-Jk6M2 6hVs5qlt8`ѝ6TɕJ :ӌP4Pղn7 <0/^իLs qNVjS31y]sBmƯMFRWOO(p#pJ,V)6ơWn}> ocJrNUVIәu3lZFmw,տjòؤ .Q- `̢8IJn25 sjLX \6%BWiL Ue˖-ɪJVޛi{UC ! i$77Y~hwsfΜ9?3T"L*Vm[X'([rx.ɤY?˔X.u&F F\er馕~ c%/ ,lۼxs~FbYp FC/U8 NIe9s64Dtc)4bcӵ[LjFfp;&Og?&α`uOp#x~ܴd>ʢPǼvHjkj9^ˡoz rv~!0crǗ;^ygfdsy*wm@ `qN`v H&j2-lzwtvMdрK/HySS: j6XZ?9Zp- ρ 4qx/S<vDD'P,.s|GiD'1yT,n``f?¸7Ytg) /0G 46D!bHر%*"=BdH%SKcؑaH6 ^u{.?Teh Og0\2p7!R9؋ 4|dAGv !! >',K읷zBpˢݷt"Óhx*xR#نW~t|8KP&4Ĥᱴcno Hl>q=pƣ8 C%]o?}bMP9l8۴AbaY|k; pFxA:E'ipO Nf҉X4uZ@Cf M&Q, !NrCA4@eȓ ~z Zar)q%sgXt6L,Mv|ⶋ_b1t&N;ُc(t%S(c$, 4a`@{'@ G!P?F(͟8*wL# I.`z,M'.135;"C( ubnaJ/@hx.r<&/ &8&Rgl1$J%qlxZ݆;3g8yZ$ș $:aJ lM A/x2aj}|GAPЌ4M8oPHNEp3<$0*u`cdyrĆt:/x#Dd2< Dqt!$MGpt(~BulFM 0ƟdA"cD`iq2#Lau# $P($R@cq&FAGBKH 8F!c AS@(, `cёcT2f&0:<(F:I* R=E$H*{ ,3[P#qJBJT ͥ2AFÀK~<~8xz90LPg27 (4:Ο΢F$ H>lc]mt؍FP<ccc'G1Xa`rL`©ctx2/r'f'G<SO>qOa#% -c#FAxa'n>F}pcz-7|-qOn_{N@dlᯭ2xp˝Aono펻wسȱSokYn_uׂƛnλ{ǀ@o}'PH 4Kek]+y ru-3W]}UW_}ux3h{xQ+@g;18 ˜|B˯*US\{=h;B`1LGkxvd;~˯+k!¶3M "dq3.=Ʒ^}P (SπY|س#sܴK/ҫ[֟ofTIhB©f @$UrGe/?'/kSqǝwUx_ q04E@x;0xpc?il:k {߶oo+ʡ㮻YS{ ]`{UO=tÏ>Mn~6ZmՂyzl+}ݺ =aãg@'lym ݰc|l#iVlX7˩x0Z= [F#zV7q%qOǯVJBQi,KDX\d^ jPXZh(7үRR-UWhb3qoJvf\-ZC .7/B6Wn6+[tSZhj͵z O*R2-6 thKl6R&j ge0G?^hzqYo ܕ3K2~D5tǗ͕>JgOw^߁|Ax㯩WﹸɎ'PB&/6U`CN7RDX|! EB~?+v8R1/)ΧxLAc0ʤ\H,z?p _ސ'+l:._(okbs°A"pE8>1t/,8|A`Z4{ch3ݓ>W$~%@7dt T*dM,m+1%ajs"Q`z_cC`T/cѨ0OL,Wʄp4Ñp" Ƃ?#TN$‘d6Rn-FO$8`\_o W,$<>vp#G4.)WЯ)kT'# NB^ve9bug׃߃K; ׼UBs_ A-(MqfV`Lj^/켿Db@jV64 `X6~aP:o̦/R"˴zBn.ck^6)4sS"Kg>i_fu^cZeZ敮t(MэB{rAWJ]5 w _ԄZ"\:Nh:Q6$[0&NCX H%"X>rbuEHiA@R*Պh|0o̫v/z7x T&#ɂH5s=`f6jMA,6o ~bUkuJь1@~ s;]t,})̠SOrzN6=!j ,Q]^(URIo05NV/ԪYF*S4+NQI46I,JLTh R^+%jݶ0o5)J\k 67)t2-w<+՛:A!QaɕbP,&Jʦ?FAfpzaVbNJRg}h\©AB!agh#M# xsҽ?+QDS}߭[_}r#uWMSϢ`ωouyOgDTC0+z\Ίh<H=:42t4<Ё]KsǷIZnF#G?88Y_]}6=ϯo  ٻpuGrЁ{wnW_|Gζ~?`{;u8؃A=;斗wm7]͕P^p9֋[յbߧ{g+/<-p/>mC o1:Ch)w==? OxSWg ;v];G민Ӡ{ ̅\ ~m[{~glh.DL\#wA[^zᙧC `ܷ:;oy͛@7]w͕_vq8j}k³L,g׶~b{c_|M}辻V=oh=q]ӯ963O&M^՗Mki6olGˠ'{{ښ 3ph@[Z?нg[1(,@rk'Ihr]|uׂsZ.8`Z~x'}z*> }bxʋmzbm7+/׏)l\y՗eo[wZ~ۀ{}r#q-p tp;o"sÃpn`ZRfLkہ_զHk{omygA -$8~gߞ+⡶Aa'8:x4ww@x 8] [[E:v`vxX ^_Ӟ3Hk:= :ڵ_x6)V8 0f۵-/qϹF)RS< Hv/|?M;B Iر|nv#p4$;[ɱ0кt*Xe@?L_oGH> bI6l) bI.`N4#@s|P0b\HgBR:pT1}6K99!i XEY)%'ɤ2T8 d%y4?EITAbLLMId:+K.ϖL g*d$)fr'l:wj zOows`΀ҩh8] %r.o(YJڄ`V$  }ZԽ^@Y]ɥ/&ޅh,zB_2q|.;H&s{1%md9td!q~Gea14.<&å&'(< cd"˹H:a#Y\$ h o6dcJŸID=6*-pp< K!lsͥγ.6kR,Wjr6*b xw%;wd"++J"[|!uG}V9,Hف7Nly) SrL& jP)7,WS X/VbXљx*BOItBbN d6W(UY$~z%_FtVTŏ~X pSgiS(fKz6ZiNj+grh6@R؁(F +-rܙ|c($rT^ڧkzXHkKKk+ O,?D3LXfWÅ| Va@d^ar+WshBTJ/8:3m5k\-5rQ/4 l.}\\z R̭~ܜd)|{*S(zc6XWZ\*6\\,WDiq5BGf&Z+ddK˫T˔*Fȝ3 t*WUJ,PHG(ypf,[r'7P2+L)*lxSrbGǠjܬS|,$Rdcb-}O[Z,kg>jR6T\n&*t8fR^NVW+D欘HvG5W?hގ]>+Bn@je^,~Z,‡@V@-.6>r]wlz}~}㢎ɤӨB"St 8dF2^I4^4"1DGo $ ND)2$f.fR* vvUe^?5uOOCcd(&Xj _}A밻DJQo\f%dC"B6Z^k^o vVg4&=d G$*K7DǑcH43>vH"Ld)gyeɍ7 6ij[@<6BFNT";5͌bDR[\ZY6lՠ*aѶh f|txI428~z@*\X6˩.yd# T P1j'DrHΊSz<A"ӛi8jđ'aM]Vc3zDcQ+FŬT$KbR3o]*!:6: FM"P8eXQN,њ-aTk-:Bj&y $< 3AA1,jҶ`0>ZP5[ujQۂjU:`R0(,@t*n|~-pBH,֔"Dy+dD GMn(FJ'p:hmx$Vg8 :|Y@9"0X$jgA'o4Vg{}‚?'31BǏa|p8xNf [XKZcȡ'O"1'OM.8=n&Ӆ|: 9LzT~Z\Ã.BGR fcqQ(ܨ叧A0)P˓&~d"lERx&+.ɀxe.>6#8XXD<'QDcJZ*.e5[`8O(#!?79#M~s|Dl$"HKeD#;,\ς;D*L}@(r;1W,Ɓ_0!3K_ 'C>性`)i< 0+D!ѱqH\@ԕ+ⱈ z`RcZ\yuCg1>o,v}B>wy^/ }' @x6V@'юNt-[=X$,^Io% J)8Nt<]d3}|QhݡT> K>R"Tg5/.-R`UzN3qc$Gِ?N! $0X"wf X<vADA&l,̇]t!DP P,P,Y fWb%)`4 `(C~ F)eٸdZlm\d2-xčpM$шcx*Gg\HXbiq7 KQ ݑL 9T<&}d[d}vfo݁si`bDxn[%/ފSGR?q0`t`׮Owwù?'q:DJeT KFO8Խ`O9{\!H'h$mJ@D 㿘)I.;ܽ{FU Q'Q4F|fZ06B:'E? 96H2#I2R̟V xjV4 UdZ.k)߾sgסޮݻ j!`j)5$uI0`"::-`ds<߽{?xof%" 790Mz0@*W+@,c^Bg'FP#trXUJJɪ ͥћTR (IF:_۷'u5jɁ R4=1K`g5JX(Htu9ȳs*Τ)ŴB"shViЪ"٬T#fR0b d`Xp$T EF:;+QZ&1z, 3>LJ*JL`2d52Q+ebN54nN(XqOב X 9 l5jH*SBRN | bD%utܼ`ȤbbvF rxV&K"ˌVD**ZcѩdqX9BLIH4r>kB(Y\ fV$jeR^YVhҨDZPd4_Y@mfeJX6-zdOψYi zQ#)e0;vM*J M`ZQL:V*h0-j0`^$*7L!JgJTv*LA'I5 SҪgEСkfT:) FMUj ,YPȨ` D(O~N5YRƯWb`W%'Hi1A N x˨6j"V6'׊#$U͈& o7*uyݼ[/UYVMψ :\"@7Z٨֨F`SrH B xŖlA/*f (i<0miSSbE'SYNXUs"A7*UZtR Li jNB xCG!\ob_x^y7Y:ʿ}+۷o=zuaԿ>&/>~u'[O_Woo=g˷>_t®o>(YWlmj7Z3׭?uo nsg[_[)us;u/}[`?Zw>_}Ha䳿nm}- O=QXOֺuݟzrܶ4lumkۂz|7w8ʚL5̻ ueqnGxv!9|,=vO?.keW\}wM/ly{|#3T' ;ܙw[f{Wބˌ(< q 8jJ=]]K/:7|=ݽ`$315-Idr%ȃpwv8Í_9j-lx­wcOo~wn-P,o`Tӻ(AP%Q@Qɒd$lޝݝ'9眪rwU=3<#ܞs9np6x?eig}Nk;M,<'0bo9.&%]y߾x{7v L ȇF&6i YM"[>bM|U_7#ܗ&X |@5# $k~ҫswv7"KG*=8_ W]{ݷ6aWSW_jy"҃ "* >{p [z۟>^|ܱ{OA2e˖q |7r;?3D4vٷࡶष7I p\x1}nͷ"?~ƫ]?pđ) ?q7߻`M ( `d}2te[:@9|/_j MnIy~2P[ĭ'lNلK/W_swn|J}QhTSmM_x W]M&߻ 4'lI~2l"Z9/ \BSw{@-'7Wߜ~ι_Dd[ar)hgy6:&:䷿{†k9川4 31`:Q!bTB(! ,{RpEGP,j/y# & 4I҂@ >WKe?{ $BR'!N}@ݳT yPQP4ő'v<ǕgB=XOrqŅ5͟RJBEP"&x Yfuߊ 7pLsaw) ē٨Ã%(8ds\U|KD:lO[9D.#aG0EbB$GCxyb@IdR y6Ht^d%HQ?Pf(uCqt$&sb6S|R <bDNL"O&s6BW!<^o8esǓM{}T6pgLF%)B^eX<NL,ӉX(8g )Z]'\{H92jVe1\^*3bO<P$+pB[@K>(HB5yǍzZ) hq_&%`Hpa QD/KL[KBco+@'[G*<l2D#EBX{υU+J1'ɤz.](Wkjuh1-6c)c NI--lϤ҅z2iIW*jQ)  s$r`,f 7*G֫ReX[;ZoFV$\!EyD?TGyرt,7ѵ#;kkZ$$<Ct02N 0,J.d6_jjY뫫#GzgZHEF0,%SL씊0|Ud`slqj*X ĩ+HNC0Ed5$%F \ZJ+ M%8A$1Œy UQ\1$K RRBPlRH!>' ힹ;W-jX{K+tQWr#|rňOZOB>/ʅ|^`TXT̥R`Jx*]tٽ8S"EIu%rF1[)Dj\.͐BآFJ*դ q:/X1I&y>S:ڨTR%ϲpKE85#B}]*$RBEV)Sɧ$+B\J$*L>#j|P* O޲FrlT]{,eRj PJEikd"S2 x.e$W6WPZt*VڏZU*Y_{QY[{NEX4'Xp@5z)jEjpݵ#G5E35=voupI*sUY|^*hl%%TkYM ׾2#yM*b/V`\X{"&|:+aۡ:W~Э'2闅:P| 4 /rA˻?5djQx.SU@В$mg7ޡ尴d86U Syb;';|t*+JԠEZ!Iݟlf˟׏4zӻ'> _I-n: 7>;-in9ol6ޅbe=>0_G:6F@{vIk5L /rZ5 EB!^AfshUN 7}nW(YtP0>bRco>rW@EjAeBv(7͟mXP8#Fp^NiL>K}],Vw&Bzј<üⰚC1ʹd#̽o81-.aih-F NuY >kpbvzf[圍,+&N.?<lv:־ EcYo(V,3Vp#ӀË>! x̦{s;l#p*fW/*3 6+TGۢ;P27;ƿʘ+2+Ah8&*:i2 \fޗHX /K D^najv zPG#p[]>i^&^=CDoŒa^\iz=nFe{= ((]=`8Ip1> b1W4r;e3Ii<`` fXE\YH]v(]Nl 3/ۓGqyXo7 F hDžY_Ȣ5,\ pf3R$DBQ^R,vP I-.w:w4찺:){TVDD04d,2B> FV*ՠ⧑x bx4q,Aq't*2<T[o)ڥ>yݹe< `L!QSzԞ)cLZpfd4Z:ʔ4 @n Gѥ E<+Ei1*0Ll\8^Q!Pʢ!"N 8V9]N٨2:'hOxgIfp^-x #( 0&O/n-vt7 lHZaG<Ә# =+pveQ_F`Zc (+8ςܮ0 Ұ`\iM%fuGi/["$ r z}6'*'6L`,L2Bxʅ(Ia,b,B@n8͉dٰjB4M2kHiAa;Cs~ ,'PF_8n&5Ϡm(psPߋyx{__OgGwǫ(x\?45?17?=<;ѩ:ѳg/ho=0TLtwɺ>h;=:Ҷ pL12547869>0xC EWGgPqO!C>!EO]m&':F.yga|!ǚMB:{}]c Ʀ&[P޽p{WנB1>8=;638268٧X ʇ pzb`u8x_>=>1fzh-L+v]l.&v~ޞn`|po{7z^ޣPtF=M]EԱk5e3Mԡ;v ~t3L-#Ze5Ydm~Whk؁e0]^\Q4FA5q&vtnT>4y`Oga9;412n5ZIXfv5:Ȓ4~9bppG!S| wNNX֟FP+ Qpdv*=D;pE">f1jr”Pp[kBD605\Gu@Өj/utut ZE#pb0:lF=$dzLÇ'Gvm=:Գo3jT+*ŠV+:d6jnEetיz]hvTݨi64Fʲ$3ώ}#]]/2eQQkuf 4z`Q/,M-iͭ{'ٳ{?q[4&͢,k f~VekQkt^~v{ϞΞށ[ }pxn&hVVTveݢRo@N3fzoh <!{;&dP-(n0| fTԋK:ׂHCff٥t!]9Ѭ5FfAij&8+' `^hMni~nI#|P9LV볛vjzn1 ufP6۪fV,[:!`qy}Pu@[2p? n(voE?7c7~N7m`u~O:Ph9tD#xPAX }3?#X5-S)\Q F!>k?#\eFu9(E4l>x4g,EƤѐ߯7ׇQ8N*s|/?I$Vs/ M&*^GC@g"~ßgAd6!$4?MDhҟ"@;ُ8&jXsZM-)9y;(dd,6qb,ZqRf?|I~6Xcԑz%z kX*/o'.EB 479<~`Ϯ!K6>QE ;2752~p/+q`Ԗ861+ Scގv^@P&!\u([|+ !"#pt^ycǞ6dvA5z gtӮ?RӢW/L(ʯ.=/"tm]Ccprc[?`(?=zynbx]o>_=,8ڹPG|tr]106d%M,Js#mwʋ=eۓ˯ _FrcOn2;?5:ֺW^x'O>܋4ɇFӏϏnW-L{[ '#<"\^{s}96M&d !zmBnAqDru"g@-d|~'ELlB,L/mN'nK ?x{{/k_k[gbH72[B]hmp y{{w=ؓfJLbpMG <smw*O݂rx6{rM7ߌZc6.۟B`U>ᶎnl+&D#aq!^|yMߺFxxuP2@^+D%8i=mv"n 믿t߻Pdos׷ŗz.:v붛-k~ D@O5/8[86+F [w"Xnắ]s_^;i%xg{dbD/O#iο\u}߸~ݛ߷?mɴZ4?;5ք*iT=tu^˿r(N(ǟ/-o]-[µۗkćʗ//]xŲ}eVB#1n;랙٭<cÊ9G7h.|/]lBs5_oG@Xh7}:s?y';W!zH$1  n8 > >s9ϝ{Q#/t&FGGNYO:wכ@kkNWrɅ $/FM>lK]qŕWU֚8"䜇󶛚#ϝy‰9ē-SnkTZ2L")&,C)I!QL E#!!lKl*#I}"i>)J!J"+&$a$SY*F0F,Xґd:+>NcH@W  4Y6,T"J"^VH' TX{h$FB('CC6aLv}5+rXivGLh!D7'aD#8J'u:Ή \ȍs" +b6ኰl,'$!('}R"h㍂ K^*%)EZR8[(X#K3q.!Q)>u[Kz@X:IEI,$Ѵӱh,@R2Ǣ4{C$"qrlfl^%R 0bE|^H00J9H9 'Hc< I $4P^RMQx$KRX8N%`J"7B$Y5DL0vHE|q"xJJL F2T8 ;l4tOikI:uz|iOKc1:+tzdmM\-kZ$qlZKD,x6Qǃ_>[ UԈP4 "f R+TP^_SBq!3iNb|X;h|T+Y!W_K3bq-liVf|j%3l$SB!e`RbRQJRrBOPU+kբ+[㏉ȲRPz}T4uVV{(T~u)IiLJ,P7xjPd-j$UkZ-sq&W)*U j)*֫j>/Am&&RjȪ$$)T.H(T׫U)[\T)eA2Zo4Z+bVWy }"h](+nRE8(Z_klŹR"NL6hR&QJKliXX^+ǥqPKco7jL,HJ!-RUԊ'!dLHR(Z9A j>rݟ9V:^@MD*JZ ^NJe1N`hGRjM*$iZTfPG+QOE;](Y>]e`#%#gjwJHTZj! м  v 2Ŵ$\ZCPJ rVʠxR~`ZJpBdJgUXjFg?IINtpb̮6KzNZ6WHRH5A+U08ѣe)\ VrMd|ek`?|&WY\Zfj$SI86R&^Crq>W+Z]e'YhP> V)l.!rSVWLQgJ7pdJBrtQX?cZ"q.%e~s21[̤kpͦ]jZ.?'\HI&WF`K*Je;v\܂5z._3X؇EVMf+;|P;.fkyX4 ` x>(6_ /.Y#1Oے c FGc:n\Ri5fE~ɤSA ]fӃE~lӪfup眖"-V=[ft^6ծ^W` 6*kDfQČv~`3i N;හP vl<%1^_(q떍.ӂ mSzta„j7VtVr$.=V5 ^lnnu:[Ryl6VmshV,nrg'v~Imw`c?04J;4u:j1e͒& Qkpu NiVnS) _Р2YAl0*m~=M^zc~)ipern'EAꗖ*Ϥwq7~fs9@0' zV۬^֙FA3786CV-n:a±h؏.z]~,w{eΤ70@:2(&Io FbyV٪qv ʹyͪ5E`86miѢQvb-52MR=?5bq9-&Ϫ7ڼ^D,&~YB>s:]vJXQ-\Vb#~es{B!qǣnN-RmBрͤQj-08' Ue;4P%83H}A,}~6e:Is,:3t++L(:1ND12β<d(L?` Nᶮ,q~R@&Ǣu{i*$&Ԍ$` >%%bob P!1i8a81  #T&FqábY[, }{EgӰ!\4I|1$DO!?N04EbXB6%./0d$ZtJD2+d4Bxd:cx <;Ŧ 1A Ʊ|j$i869(@Kzc KʾA]s Ν[vɾuAhl߳bzB>098=10ض}phddkb~pjZ1vZ=}mG{:cc{;GV#\ᝯttM, (v씏';1|hl~qjbpjjRPLt kr}mm}c#q[qoPX|tTѽr->;T900:7s{xbg{T;7бۮvbcrptu(}@sttfSt }-.k`L54{wxL1;;=63ҮRLON)F Sm]í=#~y__.]jS Nt:?8dl>zsotbrtxR>0628;o?664>6>>l]]]w.>t`Wg{GgwoOpOǀ{h]eL}}z|bT׽|kpWzpwkmwwu(zzFn vl]A^ <-s SCeօjFqᡑѡCgSEגnm940a6[fjYcq8%V416#۷!w(}ǧ; LwiUJ7kVLZH&ɨ7x{?-W62=7&m; fc14ZX2Ak@&yi~v~@gȐ|` L 6|L hq zbviiYjLi(YZԯŅ6T~pQr` rtKsZjtɮZXP*W4z-$&r~|pޮCmaEF4XWш%jL<$Fv9ls8+ś/PɇFFh1[6lMB`ءXY_2J ZeZQhU:jvsaզјЍ;vh-L+eXB4C+Cuj]Aݨ"lʢZ;/^R UFkYn̛AnI֛;tVl%IYV,LNjeW,yiqY^:-PЍƬWVZebnͩU+dԙvgfgfNo"6;2QON3j;kZd09ˉ@^ݹ9ц~F[+P{AcB趫|Wp5i,^ l a:%x%`|Rk0 &#F?ymOh֨ kpK%ʹv \+ҢCLa54*ͲŸѵR:_Uj'a;ViV faYek.ڊ3 |n juU+U6jY(=[[R`ށzSy/XN~Wzmvaev]EWpRrQ69<>Z57DFv^AB}v.ٙ=N6+ݻcF3-2rZ!+fӹBV=PBdZHIr.QebVKZ)_uQL4LT2'S\J*y0R҃hILY)R"e)VRRT.Q?2) P5rD^RXYBX,)ϧg.[H媔CRG VN k +T hÉ@e%6A=Md#GVF Dxm$+GӍLv>ȧ\qۛ*k%v4.eT֊\Z̦ʫW.&B%[Y+@r UZ:& ҥF z*o db>/b-$E>@b@++w5$ zsy1_*bRrn:HNY'%k `^aKc #E(J`b.U,%Rk4E\ưL8Ux.]'P|eRY, NEccGGj lBALڪ{ST[ލbl\du\kGTahfbRk"'HeX1_ZBdÕMWZVJX(,TȈ0JbZ~(ҩTXʦnEtDPgTU׏]mA`OrPlrFЅїkR9_LtVʙD'Jr| ,U*d\(WWWan2[jkbZ@?Gh*SR+HEt&wX XS(U׏EI֎DH%ZJ{|v2D%NW+k~9/e>ިԋOˆBbZoWW5jwYm4-vv []=;o}l}ZI (l&W24h9z ]QKp`KREV~ωjk#Y>z%g%ÈD5x'SkarIhR]+X^LZ~Vөr6S.kL"ʦL墖eQ%)6WG^< pVZd"eE4(]@ߧҜ{U~ ^)X x}_UJY')0\Adb1}.X;P] Jgj뫫Rp4Ir Z-[.nycTިՋ{%Oe˫8ZDPVE |pI*7*k5ȡo%)LjG|g̀5QT|忷}Y,׫4굋ZmO\HZ]f-轶/ܳsjw~Eo*$hL"P,O4 szA2T,@QbN /~eU~tqqZ`Y^u7,vw$ǣHfO &y!Q1K qI&8SvIHi#(0, g9x =" t4#P1-v!J `N1 Odb$.Av'ðT4$ 'st6<..p2HKcb%%NTS!Rͨ\!q@i".L RWAfts)cd& )AA):|NK ,)PS+Ѻ!{B2$#8cc YOOP| DR4$c(M3JX"& s\T<,c}aƣMdFHoi|98 /F'y10T`KH6,IRt!d٘,gNqEU ̋B<1&p#qn9D=c9i:!ɸBX{2y,HD@ cS > IbAdPCh'V@LAzl1s>F1=bHGۛQ``)*Nu(S0#0 .Ġ a= B~{ z}8 $zQFXY, ǡN` : $" 3"DR& q,!BP9yh;`7*h6~v 򙴔gaa`=.pגii1ϿY^SGJ[?YDh8a\r?A"G xP$C(ÀuXyps{~6s?.uva6NWŶ+=x ܟ=u Gx;,! G^woâ+0^{Jw{垖eF-u&/uL5̗Rj} F8`88Y\ vRO)3 M_d2MQ@9YfHNp׊Ss=#a)8^1>$'Vea$aqYYSUE^([4 X-_W+,#PkDʲE*Ҹq!I%N4Mu䟖SQ+0p6ms>,"%\(DPAIIyQ>@H*mqinxY`D,?FiIvVTʮױ2WbWyy'3h WU D2ARQf(QW5wx٣ ~+>)+̷HL\,'k>-@Y0.ʷK@du)(ALv陊Bm±( 'SR@*HO>^K ES.,1Z6@Y|QZ '4Mڷγ$c@)ys8:HMfdF#a$;`@rH:,jf.RnCpujbh2s8nM[)m8`3Byݰh#_(Q. L(C?@Z-<:"2P)pNBG#jl4"紑ࣔ\I‘QUG"p6DQ<.bVK -Kз#s!k,6GX&NxO4HB9lit%xʱʂ( SbIa<_ nsBDT)ÄI %}'#^y=سsv}>ͣ@8Tۋܬ(*J9sbw w&Q3.Eo}>uF7~n)i 0OD\hUIMUp'Is"O͵+^t0MX.>y}ۙœGںa O/Y0̩g`jvZJ3SnU+.|xn6HI>s.[`܌kB:G%y)?)m@ڌr 2ts4?+ބ /0Λ=cDL |Q$38 W^|fcfߏ5F\8AgX0ooXm/FتsO$!aXAւGA-<y[*D3Oe40%A_`i+o (H AE?{O$Hѣa }y FЄDA@ SYU,swX"l9` `񿎛6=hr{QsM√{e;kڤ{~5QZ2L={˫EW980P_Pp8eYwm^Ҋ'9Q 2aea|@@i=HhWznqp" xضy L |t.̞ gb:2aT܁\ o_rA. :21s;>SRz `zm0ku 3"v4M+ B`jp֦:ae!#wmwF>p"㰔co7pvI9 R ܕHuCo O=N1-~BP_xcPxVHaeH~뢫>R[Q ذp6 N:Fc%RQ&t(dL ǣ`!(DԦ +j#;p< s^w̍wjL?8Ui=L6Xp ), }+a|CuFq Q7,Oc3b8 㖘&׾i-XWaX3B֓ ϙ"N`nj45\1ba5S~sU XʋrOܵ5y(t܈p4alJKڷ^PۘxX= ,q ҂r@ɍ FIU0aYę^[uUiQ.,`ؼ~McDx#S!sBf=Sq$ܝ\O=zh cFt[-UeX*qvS@ dzq3>3,K3+**MF}a]U+-0(; ),EE➢‚p:o 1b)4Y NtNǒ)IܺiY(!\AݣI S,#Ҏ$'%߽c˦k_\E$LI#ˇ۽3aukW=|G|~d57Gd(s v(}{vܞƦPU ^>|yFbĈPÿB L6b=J=p$iښPt g<0ϱaТLJ0 Lf matf+hס} ;h?B 83hBnhM6Z;vDil8BvF2@֝ҕAlJ,L"WpHW>YTB<Es#c >ܡ>8P>CIVZrH2a[EPBX4&L_YA4GOr%S\EEn,KwMVNkp'bf9g-BsW(_j37LeLȨl{L7|*X (R/7Q*7r(НY⧗4SJ y .P PVXJ=nG)yyEt"m^DʗLKsν;Gg71o &E{M6֐{C^GBRevLHCjp݊gj=wE/͋Ww}`o㋽_z=w!\\b͋Wwݶ7?>Fѝk-J]ڛ}Ǯ?Aǯ_m[n3G#b}Gl{#ܹmws}-}ww1ﯶD_^ꓟʽ]b׾_}O~+O:_ͣOh8̥ؾѡ| /tF^پ7ٺFWwə%9߶oFz4>zgoǏ9|L[zt f~ۮo|׾*5S׷A|;/УX3'uF́z>|֓{T;%P+gͮ=~K_ɓ5=}{`hlrf%3[l-ڮox?O| _Ϟ_g#gzuM^gܗz5(]K=v4 |A}|+~?aG?۠zht|rjE>VN<'3 axXn;* K3<#鿊QJQʳP(d|]-U'#~+1.Ή$)vhDLf9U iJMUzis(2 GE169W3B"/hg|$AD±R"l6sWU/:#T BBPEo 4aCRrRx|bVI=l0(|}1LFVrlZ&Pf"h^yYXD$VMTRdg _r.bR' WoAoMQ,Q8IQ_E-HR8R{ND$O&Skb„H(T2!⩸ NM$Y(*'D$D%M)0* QN ei(_J(JBɹ&XZp8l!_ie(œQVR2Gb2 6Q(AZPr :"ʜ[_SR /'xV2R 1yAx* fYi=]$' A'`I$ x!`M[̝2bJDp{FqBǰx4EuNW4l:[}\Ԋa1yI߁yEj>š^$ (!GC)|K{a| dBJea)hI$#d"l"%P"ßh$.|MGh @F &FZJ*dzJBH%,I9~ݞB'|HqIRH8`38FNq.$r4oR]@cdNIN*gsJ(Z@>/\PHL /Hri֝/BL:+E(@22JF$ǓII Tf1O1݁a")N犛lTZۨ9 <\&&R"S! k -fr|&.)r2jq1Iy8 3f"dE½dR,dg+d1ZE R^V b:I߂=(l>]])r2dfy+b"2qO q%~`σ_/f b!R~T]zPfV>Z(2(j>MFatKR<KR` IJT+Zz\Xĝ|74|5#+^((RȤOV66W_RBP.gmYvw71Y)U^Xɧ|Sh_N8j)]\-fOFފ6P\VJV]X&q+99[Im^|aZ̯t߆ݏfcv k )2k*+߫ܺre}51N Ϯ(EEANB.&%_]G+*7i*HDYYdIf @ H+l.ϖbѳO=edFYY<_Y_]ͥr!ϧr%i@o d lR^(s`VX.o^z{U hZMʬ@ǫ>Vpb!]Z+(\~}bP;$IS048z>_8y|۴S+d@._.W+k[GQ)JFJd*[+Y!Q\-WrMUT)UrZTyLeR 3z:l+RDIKK2LZQN܁yyW^oTdtjOU4FVV iM7JY`4n86k < Ei舠K]F&w {n-fׇouG yzNM1;IYV7B@7' 8aGȺsr;0ØѸ@GGkֻx,BA;=nG#OP&o0D!hry=eW#|}e7{<6a4ۼ' 04 nᘹl1׻>tXvżu/ZGc Z69\üoqWn݂n ?Ǎ~0uFW8MO-A8Bϖ,FChvf21,l7V= ʹ' ]Akv4SKz*b6y``}/I8F?2ANorF5tn\>k f-B>c^ cP}vx47_&0@ ;m`Cf'\c`njVqw36`Y}(}Y "&[(谇"aX&<i@#KAC>lHVbB Zv 4߇v9 aeNњ Cۼ~@,_$!S$ 4E>P&f[L:WK./p.7Ei/Nbe؀ TP)+,Cވ2y`3nh Dv 1`g;Pӽ$H#IJ3X(@CG -^ k5k` An~Zѷ7p<6#x ȸ"&}-^wnr܍E1Y0Ra\xG_]2a󘦧V=\9$%hl>#24MF`(n_Кv3d (,I.E<PQd4K(Gx\I yMV_k=t^Dn 6jWeDB`~$jIE1 4®o$lRE3$H#ajBRE F Fn}CFH% )FNKL)cᘀA$|oC{N14gI hq FC0d8& xY4~ϞY @1 {~kuOpw_o@OZ=y7.LM .>u`CS#C vNLz.ttw;œϩG&&Ԫٹ=oAX-LF:UCEwvjڅ Sm?{+ɾ)]TO\9wM5X0470>=9&{|{uoGg_`s}[~go|lpgI OF'T!@O#*UgPwgȸbs[ujdnbX?:<޽CzU]ÏBɑnp_~i@:v|᣷~ϴ uw7#|FF#:FFz/ut|{?nd{۹Hﱯv_:s܅ xt0rF7tNرA5hg{@}񁷡Ãnlvjbl|R}{xwo}~ndSq"ٝ;{ͅ΁'.w Lt{zFg=3zyr|wrf\[Oyiܨzt_Wjjg@5 8ڼ߯?sc`؅gӣޞSGۺFΩF&'>p'ѢPuLvFz:zںKjpwLq@]ԣl.:{щɉUHJ~4wFtw]lTMBMNMvtzàR"IƠ3/pܹ:#]##3#;:޶G2Zv٨7 lU]m-GO4noST:꿪Uejq\zTuj{zaUժG;/]'f|^oĪ1 =si@=\=@^J5<51ѧROM.N .^r/mH;PYPؓ;zڻт?qI567z}`پGnGy^Fnζ 'tNw΍^801uiy#4Kͤ./.GƇ;UmC*U_;8Kzڎ_:}+jxZlX6[1q]m]*`_W'UK9><94nub uK s fC?}T':FRx_Ϲّњfx`5mNl :]7X ǺpyvfC=C:C'|Q MM5K }gt:uCt{m:a3guVh {fX8[]Nut|O&:X[[814\.^|Nݎ3!zDp#w!tbOM-[ɪYX^YvoC\ViN{W KF|6˲~QgL-,@#U^kKcy봘LfӦx]F]3(}euN :K2Y VtP_rۭ6jڗL6qijO=XG{UKv_0Fd'5a1a~iqxιao,Ml`ֶZ>YhXդ,/hLSCCw͡>:{cnj^,zI7.k ֙B?\X^r:Mz.VӁ jx;K,!bwazŬ] vT]62:%3 gje}j.y:S7g ,*ڮӛ,DB^#td-ρ[y ߂е2//rshjK t mfXX\@n:vvuF#4Kv`_mBק\vafFf t}ӿ\MN憐փujTVorx֙o#t}Oz~yN?ӳC_F@mRp̙{_qwi/RA,MNڮ3AZQWJJ#3Lvya,oܩ+YDkqJp ^f6nyyyһ>j;ݜVtȢt8xƗ:ˆ)zn<|Nn8Ǣz!ЭNr>p ¯ma_L;pnVhCE(ߞO{;aO;-s1~"2K ѽ pbzg?ҩ׉hB/TɖHŶДV2DOLJƫ-\!~k xƉϯ1̳;$-_O -IB5_ḵԯw3ߘ$l&fǯF/gM z~g䘪T|b_s;cHJk{?&̟uR\L@K+^?W27W|=oX߰rOAKsϜʆz }oϦߠL* Ϡ (~Isqیڅqu_貹J~"_Sz,wMҝϜ8\f}~O'?Ws_´E831u#M]ھ/^z>(Ni1,N t]:whS];=(+۫O__iCNG~`8<  7%+0LJ:ϟI߽S C,Wo~/%6>;4EڂR3^<}ӏ(x>7lkI:PuwXkilv@lſbb wFZuCO?|;}w7臁.|>n$¡v@/ ݗG?QJo|ko_ƽs h`= +~fdx %g#}o݋f_nEL%0谙g8 L4r4(LgdEUu[?3/2Į[I.J "1m죐!#ByE y1۫>"2 {b"#2/)D$Y>O,/aq^u99hIs M'd Y$A5 aG$WIMVUWW&̉Ǣ><t8grTM{c+DjUD jWeXN Js8-q\"+DEjWXBõϛrYp #&s ,0nge%ɥD>E&/ACl{r%bKɜ3d BSgb^Ybe*1hzyLHx*F#O I3 JvaEXWkgRN08)dx<" { /cUZr7[UuI|^dp1 O$i1DBL"x1-Ũ4/~jഷr%MH 2H`OD -p23 !&U#2uFTApb4˔+E:Ct9 J,1=:'W(GAEAeO>o8R+|\ X|T.H.[PbJH>_J,GP,@0D̈$J. 'RAɌ'sHL9]HJGI9NJ$h6#|eS q)L}*TVb.S\B4Kgxx*y|aX<(20\oկө|>W.UBʖW bPJ8v@Qg$`aCa~뛛W67^xa*8*`#feRd"I0s#J|u_x盛G՜s+[_]C3jeRWL.%D"?ĩ뫹Re%+ml]7+kbqes 5XɭVjϋLJqEIY*[* |o]T$)_}~cՕB^X-1k04tV6rL&++R2 ut/+*[k匜]LJq3#HL xu!B~%y;3B<(lRN R(Kly :]Q(BʯAJ_Y)⩕-z1 A?GǓyB1\}n}sjs}J% C`pjչqz. EC~l焴:}|\zXG֠Q h"L X,M3j61 6',N7x\@Ȳh]aC %Is 'v'>F AU ӴcI y85MnrfҊXc5:}i vˡ#X$ B l vX N er{D'e|!^f$ A` v {6#@ch"Z,]шc5}1z 6fl1Cn3Bms?Ef_X.Jp$j^d,iVcs:fWrgMEp`3=&gk5>ӂY V麵KfjNDj0nwZ-3Kj=gC^:2=FMحCO4 k.ݒk=.2.hߧ!At+{/0Klf(nXE݂!N%rgP G^݆kM@ؤa nΨ;v' >,kDJbc>ݢӄ=No$ [bt>y oTXTJdQ<@o^GZl0f._^\Z͢\i_P-.̓)jaBsFb4B> @h ˋ3iYoZVdr8&R*N/v%@Ze٦ZN4,huպv pZRq1F(N ~M1`Sp4k~8!M4{Mh6S9,erC&YBWE3, aX è`42hp،&hFHs;tU܏$J6Ţ$ZPv`$emXdh&N%3\6 GЫ\dA?JD}6Xy!DBBAXQJ<\P1b D|H8vH,cAcna!/0 A KCL\hH$%$bQx*',RR0uE12ʦ1ē\ xHw@%VR8RH~W`%jac Mb^&hbr SHxebHfLZJ(Or\W8Ǡq'1'Ht6H#A(!xwVHՋT4q"G1],8x&XecQb3BRӹ4И#`;;`+ h(a%.?HB,bYI!{#8ɐ.dR٤ dN,_TTI #!P%aiaq$ ]dd0($yZYq"s[T4d$LzȗVń(MŔX8, P@0 ۇ͂$B\ }w`TU QDDXcaCLdfғI^L%􄀮[~~I(>=sw޹p8 D29p4B c>KaT$]$N1,(˘{ñ/y\IC`Um 'T & ө/D\Nn0 !k VDټD i`ݾD 7h~1/<O]Vk (޹+6;{O2 xAx"NQ7yD\^W+{<ĞLrDw.|2@4%s(FKo~ۗDd() %@H% ~υWNeaFDOD :c1W?h޾.rFt" q83.wU?>KCwlY \H ̲cTrc]D:p»? ``c&idj\z=Sl*X&på5Xu< }+9:fs _Kc6Y*khlZ-fZGz >.ldr*iCLq96I7h A*^g@Զ &w]K/~Y[U]ʤ\PkrNa{#ͦabkM=įp͍L<ޛO߶'EC4'<PW`7u:,xBA֮&^]]euO'k8:EҙLvb7 ʫj-ݝH[f1Gl~QCw55~N=W"Vl&ʫLnIH9 8=5|~{&>6h:r=ǮjU{+--MFP?*,Z  aij )u4yζΎaM ar!V[  MB>G  5a#ln`w ;uեU|.nI+`ѪM|6 h6t&as;&q {jЪ5t&mueNV1kʊƆʃeul Vs`mp۫}lzUHv[8ĭ1*Ky Q(rMR_YjdVޞkk ta'ɡ fMl64YHsTr90uJ,`52Ƨ95vhW ˯8A{WK%2Y Wid Y}P*a1j&eÕ5*-8u@Q[,Z=CtMut&Csuu<>ZPSV^ZW˨זvp9mM5U4:Of X+lF-VV @s{kYf6FΩݳPSȭo@~GjX4VL#4  k@ȸ@qײ{:N?t(贪jZJoBC$3Y#UՃn MS:fO9EuҲRB*kj PRizY;U]ZVY[b'X s88l\*425&^Rd*^;I+t0mhlj2*lsYj6٬&fN-kzV&L&D$K;uU\![nif<泮dV6X0a7Mq F$VZYFBiҨjEH9VnC{lρrN6Jm&mN+ɡoaT jMHCjEmô.)CܤBp(U)z$jfLNg4h:\$2E"DRlT:[j!E4KdjJkA!jktz٪:CwK({dJFoh%=Z-nd=bZnQ{Bih"h3h4*qgějH⮖v_MfJ"= ZRiujC52G!Rk]bFm=ZB_*5f>B1 [R˻zEgXk-Z`:L#^J&1>&) F`7nx%ʌ&#&[,9jT \m52%СCxUjQ ]w[#h&܅WcPw2h 0O9l0 iHUZG*Q[m&N.uKTSZCnm7ZvZ ҪNB"I;ZV.v3&R$=zA-+v])UjaZMݬS`w8lfB.JHfh6 tZkԚrBo@Nuf pL)י J pSk3eZOUzR"U(@%7jLUfVZ֣2erRg7ke2dRwwMm֎0jn+pQ5Z֚͠blWR@UԅI/FOV85mdSU-mbJ*Q-;6T-F$1`7鴻ZBu:&YrXg= kmi,J!Ԫ.AȔ_~˛U- Q٭ F?FATʞN e֊;ŲH?,0E.`7ja1ȁ\5X  kaPį[ŔcVdi-fLQNFY`piٖՉM&LFkt0gm4bX"C⇍:Lҙӟ@EF$5'Qr)knڱN,W6 VZivejVňi?਴"aybĴxERXJL{ޭ6*Vn{GS3_zUG1YG,;Z5EGW[w![\;qljo3OwpǏ[x7/:qy]n4$?ljNwvUs_?~_ 6+h͉+O|[]Jz=B#Uj`C|_'6koW3؍MXRk0Y~>O\tb,i@)t NrG7~KzjǔK^?Q3`_z%gy\,bIс|۬Ooh?M vG"LCYȓ/N<܋*cskة02DH~ǯ N0=XZIcqK; x`61@}бv>K.ppEu m SvtzĈG},@=莧{+c fXXy<$n; l.IKk{ ENR9{p[q7.ܢ9,@R^B"I"q=;qy>ڳe啈dĂut1d̶[o"pBI$舅 %RF"F`"L*p$WB`ncÓcÓGɳ))9c.{bp 9 6L:=4O%W|s$cБO>>UFx`"T8Rh G#AtG3CǦG ccɩc??2162\2=]XvC}&Q,.U<:9>:2qH>Apkyd.N]pl(wd3569ɱc#X~t pQٕHFp?0ԥdf, &&s#ӫP3q7=&k4=8DbC0/0iL_dYp8y0w9C'&ƇǦgLL 3(6OeKҎTi38><46yώMM ƎLLɧcN6N~G&)tțL'#A_8)rccщс(Vt-$=Vgb(I<ىtvh MM冇gDabtt,?:358f"t.#MәT~B[ab=:9:1N όZr1 H*Gьo7&cd27fr " Z';8l<6Pao7|ύ CLjL_;B)3k%##fƆ3c?4.,I('bg0dh<`>ҹLn`xjjt?15/ b;-p O,yT&F 4pgb0[(k7Ng;و=Lft|Y_c#(@ #;2 [i`$$ndȧhzj}x*2< p8͌ fGF1'm|dlx0lE^O<q`c~/Ƣ.O|d"JCG(G?9? }d H$Kdmy|O?:69126ILN) ea.H*c$EsǦi:,j~7luدA֑8Ob>@> b\oSx0 ;Hzp8z}2o䧎/LLM `P( (=^\Obir|fzl8͎d~~$e&7qHHe ð'~ (Lѣ'7=:3=}'3#`dhp ?ZȤ`fbtlhrl2iSG'fF@`a' fSDEaxjzsԧR96q7`0(8E8DCH/su,X@0&q `B GrѱL6Zc30nFɱX4;:H@"72>\ 9F c#CyWLL= t~3>s*"5.Tat$C+7vd|66Ta2?fj;<@aԑ#GLONŧ3ǎLO')(M34%cr`pd|>G>}Q5& 1a' l<Ϗ 392tЂS128Y?O?m_ O:X018nP6TY'Ɖ faə7DlHȱə!X΀a`d88NDH.CV* eb>Iٔxm6Cd>M fLc`TUDB#SP~t452^PK<~܉0,LHH=Hn+ 1pcGE ,89òD( e| 32a0(cNM?\ne3џJg&Mu?rLT.0A1L{ L4h&Og`8;~tzjfjd`l|`#GXs`@al_|29=B-7< BhxF\x25q *i`ȱGPm2I|RqGPyٍqps d܏ih ?,Lj$Ꮐ s_tF`f^6~P*ދjne"π# %~#0ˀr}~nRInA7C x/YPx,w:l jDvW; D p)"opx C!Z3h*ZTHmmjHal}p4Q0X$$yRUUJJ)W >N%+f9j7L4N%H2CXCkv5;0Wop8fl*!Ȁ!] @,6~L*vUjNԥ:{f#NDZFd83u!fp>QI?,z\UVBVJ 3h*=$F`=CDP^dx}NKjsZ%^\M@,gdn ˧~oTnvͬ1kF$7z]fEb`, S9۔W-\NN;#a]G qtL2eDPlwښۤǜ23 :o D3*&r#5tJ,l7\wV~m0-)+9ސ '#|n)4ndw.FWLKb+7N8!"t!t Ob=J^n H" +) A%"p CN^*Tf$I.]Jՠt|, 

Uѭh:) JokP4'ȇBP< BQ2ӫXkj79FߪP{#<[>݇֩7xl.u`Z'@Tݱ\6MD|T*N4frR5Te{ t4 {\l:巹RX 'yL3lzV/3inMf'~KPѨDŽJs{wkc݄!8:W<z\}0L&~Ø, ePjGפVe[F>]F}zF>gQ"VZ]^LlD2&"gs^7pd)~Ǣ{*X3C0ƒh8pt F] UӥM2cN#6B~o_+V 7H`(Ws1S;1#RR!߮ʕj1V2qPDf 낼B1-qb nh4, yT, Cx 1@GOG+D T B^'yrA{ #zD!G~p张h"ɘD,$>| u%aXp/M.C1pĀ`_98$NIƝw|Jc@DbaN`}p$yZ7(l*l' %pϙd &(9I#a;`-Sh$#C.,v9~O?̏OH.o %s/c> zH.O~%_D1sYހV]P#' EK p*s>P>$.LN!hL:">k_wb`fNK^PO?F,@I&> B ^wѠ7y{|!aBa'Hb~{_(6o<p|0` 譁\,hl6z]0ۅu\~p¯!L ɑta31`H,f0pݻ ! FߦDt #k+:3I |@*f x4M0DIK9s,F`l}⤐Nj+c P$|~O =4/6>ĩ % Cf8O l݃: PX2bg*D~"G4;OEw;F`K{JܟLf{{z2CT6rf0؍B>Y hfә|6K`2VXM5ֶ%QDu"lkm~8lkjjh :AvT$Q*0 kؼE*U`N)*sàҙl}`MƎ",/9uJSVds*UtVmآRifI氀`dbTOe2-/W֔P}x2|6֍J L5plw8FІeUFV}pϨ|o>uԸI4 ;c`,Z=f~Xe6#\*2ikU V jZcB&A\_QQU^d_51M:ɨ(uFEp3MkLk3Ad񕠓`@WJtZ=ŤQRe`/u*=%QUt” m¦fS^%?ʪ%&Gjҫ&d. AQjl..`ۅ~Lc k \!Zeka߽u6jk;5; Z!ds40%A!b^n@e?]kMDӟM|YݤS*efjƕqjkap8¦#hL2,`PvtHҞ6J.׀DZFJȮe~auC{;j䲘M\^yXV*1yH!QZ&GHv ' L:eUUTZTɻeGR*e@=X=5l>j6CUB#H%`(0B-}յUr<P;P-sK&8"fWD2L/)sXUTpZFR[2*5bIZ! ҨUג &~uY-]Ak50?bvv X 2HkijZLIazVssiu:&udz<`٪p LT&#ںNM?llo(cԂdviЋSZ7VJ eMj6e,FuîfxSnyª3[&jhz= j U x,^ϤLZB[ FܫT IDv54vrj@c0 rIiEZFHI*(lj5殶Fp%e5j l(f9jդ1ڪu j&ف^t UKbJo<Чvg,kjXPFbjkAs"V9(0rϊys<ְm<: zkl4 hUNAThay*A.Lh%qY Vݝ] LǢPiwr!FE5(b Z:xJP@/H L\Q4j^(z6"ܡ5`V6uz}mi)T"jn$ܤu *;lXbz:@107+3V[]Ն&5BV 6l`uBu%hbʞQbl(zV1)suDjtvD!`]ZϯWֱyzU~K2[nԊ4濘'iI-m|jmV?(7`ִ\u?"5>iTHUGL*% _WI۽[TPT@ʤ J.o+jͷSeV7& @6L^]4UXֱ 0ddYbEB7R4 M$+EbFӈ_0Puu+[iY݃we=R+sS]: N PݝmRHpfxA)j 0 jhQwuwIԨerle2٭BcG& Ot^D(:RܥTwu%xDyxݟҳO>'-<ԗڇ`_*,ݿ_dz/x,a}gf>ZއgYSqrqWQūc6!xO3֞ < ŝ-|6hP{ڛyxZpJTIE|q/+֡&ja"S O&'?'?+KPydv$q旟| +3"1k\N8:]>/Ēck^[@`2i{N?%'f`ucNdΎS/^{UoXv=g%.?Jk)vu\uť.^d'o_Dt5ذX~YK%Tx޻ny+4tE. DO{ 6\f+,C'[lK.Fxxo }̢ XCx捗[sS(.}wrõ^fqjwx'm[rIrTlۯc}Mnj s]oN' s#ɡݻ|DsZ {"G“w׶WoI\_W{goaUaO@i6G6_s>6L@͗~]yㆵކ][q÷_yz58!n=c>E٦{7^xtW!{3@[zG[[k7hr~omx@ڴ4Wlx~j3KY?ls t!$gԓvύ_5mf8 P \<|m0y_:'bSz Wod4=\ן[0_夠w0 {}'D|=ЍM߹a٤4]]H|y?$h~'9f/$hq&O2?(,$gxm >C`1ӉtA$qo,NӦomIwcV)JN63w3 `y'gY;(9SP?gk)/OH)ܟ} N}ΘObpxg>5= nJяnP\?:t \ ?F.|R~r|o(_N\_G;랓pqd /YvWYwƫ?^/*ֳ8<|*QB.`%K^bk/k|~_~w?ʫ & JN)vrŦ-7l}}=>ڻUלhY/qmvͷu>Գ/oG{<\FRB=rsσa-[oO>K_}{/`GfXZ!6]sݍnzO?K["J3=W\~ yW^}\\.h݆ˠ vg}#Kfۯ^s1pf[n x,xlUk֮+6nzu7ܸ{{!rysW\|mm~؃E!EW]pފV_j 4/L~`v!}뽷|qˍ@?Y]~9IP_,o;R;o3;hOV貒e?vR7>Ï=c'P8^~W_W_y{zovõݮ]9˖Ek/{#."uÏ>`yg{{g޹G&0o˦/?笳..E4h"ybu/xG~{ _ 3|ŋE52Vo 4w{|x>p}}m-[ 9,^p!5bsv j ߞo6^z:`y`ЋבU@]{=`mn)7ݰ-Wox99 V{6BcTb :o_w햫7_]tJ eKv<VT5`RFi3j_ R\,g  ! h +HH\q}%7>l %A 89bP7~u]s!#׊,Cw%HeIyk`u 9v C/>!]?O-01-*P+@AR[H4@- u7$o` V-88XX\g!IQ8? -Yh z6q|r/6PĂoN•P_Ph%ǗYlbٿZz$kHybjQbޒKK#f墀"Ra .YL]t|*3YbFl`҅Д3,,0_W."R:oޒS,,*0o~"@,9p׈e`/AE.2b=bJyDAMwwV2諷~w5* 72,>_xo.bx9+&:A.(-/mNEi-#;9s/ `!iX.xզkpDs% { jRp0J}9U$=F ɚSD-BYmQ~~љdBb׋fרqηg:CX> X w$1DYrR1 9ˋ:ŗSc(Aq>N:ETzV $ܨQStcz 2QR-K/-ꔌBb9+sISxߢ,?y( N^`'ϨϟM[C"J. J8[@Rn}fu%qU/~dOs7(s{߬͝ɓON/G%td`w&2#r{v:B؃w%3qE}ؕylv0`7,yx#+/}-NsH~`a/bɋ {) bжsO>ԓOܹG~1r{cxIl=Ï<z ^yl`OW־[r/q}ov ]y#ۺ"`wۭlF6CI9CDOU qk6;{?iR__}5[\K\Kl72|h\qeY+6+W+6LJ3ׯN"<.]x)%#?(iKJ@5ru \ >oOh<XMzVZ2G.1$Χ^0o%qq!hm8?f0KV|ubLB9972*4]pv9 "8bɼ3L2".]rX%.^'-s x-]y9'KQ,8>b9}>DZ/qXq \H06G]Y`/RՔya'6RVׁ#Ҕ< _)yu.}r:ŗ?|cQ~|poNݲgw̽vSn7SCt29zsy +/[z*mbZ@z{K(2)a nnF2{bP|;&_\CY1|AvaƆfn>VA%6Pw.[$\L>3RE>‡IA?.KLP>sgR[.|w٧P(Va&S8py+'*s<|S1yx2%ŖWPy3pK:F,{Ur%\E9yZB'ur&6QO|ıBs)<Z "b+eq#FWG=pPv+XIs}o{~%;:/͛m`wf#ڬ=٧YGI].)Yi# (%g-C ѕ2!{8GUpQF8h 1-9c?; wordgrinder-0.5.1.orig/extras/icon-unbroken.xcf.gz0000644000000000000000000011670511523516526017110 0ustar Z{p\y?^e[_Z6 ?(S:@lɲZyMl8Fl[qIv6[]l\&lCZ4Ɉhe[oiι21g:~;}߹ilwMMM!tO30Jj LW`KSn~FزeKC_6jo]w])SM[[]iYlohڰҼue7ZZOuk:sbK, gYLO\}CuWq'/q6˫77-6mm?ҜKybѫ!1ܙ"717.I\)qqtW׵0͍E[ Ng߰_:]1`9PXUS \N;c p%Ec,ıN9 g z*]'@ثIu^a:dp&;-bϡ#d=#űY&AȎc@WNc9/*}3ձ;8ςaYD”Vـ3uűFX#XQRo#߁-!HbaWbQ"S42[,v,\l3-bG|<2[:c~U_]w, pF^{Bl,c?IJH条Hc߲eF2' _5TXyzX.ݶ1S0eϷqL5>`Dc}aW&]`<(F.YbJGHg:\La9;uj 1ݭF2U 7!tpBh˄UfkB! #F9+1rA9cAOv4˴^ͩa#-}XO~Mӫ8Y@5,uZ dayU;ng:Fp=A@rIšMѿ!5( typv<ӈ`o;Ly CS$\݄2.L!FT (%!\{=EJAP2]i M.]G^ .$."+$V i~WD>0G 6{sf5,`tBK R:RgU;s>=L#9\v7D|w*9$wl5%➁7kn$%{yx8ño#y_ ʩ9$f!u(:=7R\FO;v\, ~r=~+p)'bA`p;DBDΓvm̻!9*>eE(ji&)"`@ƃAx+{NU^з~"-ԅz@`3Go~=.s\񗊅}d 'a;,Q,n)9&1s_B?zSءߐŢmN>٥j,j .R-ɜ\u?`9^?U)fpN.z f Gg:TuOZYA}۸~6?*x82Eo&oȼL_s++%fs|@* *=bЦ uƸkUk;EkW];@~g[GV2\)($ 1x71] ZVyא#_!/me"'`ZS{=XFq[$ăzjr"AnO^~5<8F l+BVӋߴRܡGXky ^_uq&'A4Ϳ'dt0va6u1AnR-@YCDЕKyFFRwt*~]?Kܤ'eRkIey[لԗO%jWŁvQ-Ec^Q]J:*\M +N[fTM;ߋ¾W쳗8kQ aWLW~FjHӿSVyI\)&8ZʚG+Igޤ[wzn繻^"_g]q8sk"jrUTHo%p ^tgx /G&U܍ư3q_.k0bb7c'SBT>-BĶO\>KҩB'.9OuW/Z}qFWe~$G. p ]B\@ ]#bҧkDلC21{A{,*Y(.vGv>}rn7yϥW|:Fcfw--t>? OӋ7OB~8j_ **ėBlxIZ*!^ q+dB.\ȯ;|H }ZW~HgLGG.Ig]]L"e #1f3,΀2Ffsi#-CϤV PI|C g+->t&O#AI2Pӝ _6\8k0=,gF0 ϸ BFZQgz¢?%e<#!p:$Ev ,1ڬL#~r+EAvDC 2mSU8CAKOHXYmex(AFNf <̍t1$C{. y{WśSAX ^:e#v%q@PdJU  3LKP>">L[9;.놓 AI­f=P(d* R<'F04eN K!Dj M›%,j}52=t}|Cf0T9==&d *.OհUŸ$WRD|Ԡ^eukdtFLVwMڭa#۫fǪj04JN!NVdT )DGN+̈́nY4#l22+e=x,֙Ç;ch2Gmmd25O͔Rh4 #fҌcdR2ٓ;)/nb.f:-׌Eh[[4#n ǒI3A>̻p$2#XC` 2dJ tܤtH8 Uj ;3cң#$L4m uơ0 鑌7HLL _ P/&FA鷃!›?$v{O>ڃPnS χ?c pg {C ׃$yM@ h2`k` .PN`K+Gq#8zH`I{K>}җ'>dCɕ2h)Jh?~NeP֮ڌЀbc]]fNҲ 4y.e`0HOp B!/`&LmP Q`/Si'"B8Oq#h(yP/,op&! r(g3Q_@en8Dlp@.M >PZRWX VJQͪtjk߅J#@ ?2cFT: 5UJ ဆM=Z 2'PTE飛~xCꯠY�J;P@+F]"ʅv$N ]Bu:`IjBApw?0'#ѓVG:zvtao6S4/:X[:i*{ϸU}ft#ݗd%{oqÖ2 K4toJ]aq0Nʷ ^ISO16: lwc^_m#,B¿` JzS4^MqCF6h~FudG.6cdȼisbhr[⩹A{pb`Վ~ `|2σ6ޕמ!N94y~ۈǼ4{LPhɘks`h?n|9ms~u뼒_P c XAHt{hFv[=/-Kx1[GǾKN|}4jF_c\Bwţ' 1`{!ϿőAu11ƈsB1vWA.tI}_ =`|Kc%8CZ20B?a "=>當N"D zYwl)"F k^ K^̫.r`! ׈[SQưktm]YxK/sqE.=2aF 1`7?viUZ >݋K"c>2@4={\ͼ djhEnC=epŸ`Y81 2f7^d2nm#'4&20N1x.8өxR8W$;P6ZyE ŜuWYX4e L}@˫ ا#/|@Ckd8d_H^!G^dljO" *?jhglJ`[n8 #:C D(75 *"|Fk~} z(Ҕ1^o1`ZUJE9M^ Bə4DS"Vwk"':^gxŷD j5KjIۯU#K^uPί[Q- DiZrS X $yHc Q~4a3ufE$ 2*j/N(Cn}B*rRmAqGp21Z%H n2 e ׽#X͂$a|.s L( X`M#.bbv~Q!> dT`ɂ_Xe?ܼX sp'|2;TN ?Nh"̳ d9Ą]/?Kq?!Ǹ_*#6j#7;B6@$m}H!F G,H|Es'gʫAlU+hLAXL?@zKzҞȉoyr\SFʸ /Vn8r7ϙåFMqr>\uHƿD\k\xhlO IY 2>e6`sT8c+\& >^OnP|̈8Fwp]VGZjas\:?;*,0f>!G#䔣*jyy^# WCSA3A#y03, Yg<呣D[8C\aNcxFԹXO˛̅QY9y{EJ#Z01u"rㅜ5|(D70ʩ*{Jmh\T ~q\LrY2 C9Cō BN8:Ȏ [ĐO:(}.nz24_UFOFQGh}ΫS}!2dNrą8ش sGe=H=?`<+0aLecfp% "NNe%S}Qeu|ΣsLpy8o^r+9M^_Lکh2 Kh dhuKC`/s+rueőɿ0tؠ gONBXz]Ēɛ4UieA3xp4K&Y4cv%GxΛC/sHǬt-3qk]{?g yOp73<,[צǢ V%MUNΒ/Ow|K+cTtǴN]M?>i]^oOxwe ͪBZs3} *U(i^Z*n[ $ d]s3e|i0%cpe2;tӕ1:WM& $o6%i\g zPWʠVKӊN 㜁KK:f؄̘#s%B/B}|9@QU^{)_H]r%neΑ;K_VUVCB`K2$āKNLj:6-u/}%mfee[$\8u>-u`"ir.oBVR*]40jQƪ)ӑ4Hؕ4"ٕ5p锭Iםs-ep)Qٕ0GS7SW)@ڷ fL26=[pˠ얶;jW& JxVagMxŮdzG2}JSt[ۖ>`bbO~)OM m%y06<)w0$'[e^EP&Kg w5U6OC>~!wFk QόA.;t1MwQ } Մk5MS>cxYFϸ 8r T%p=9Tܸ)>>lWv[k@i]hX39/{Cyҕ3KܮK,pmI?lʍNP?l*1.eãl#cO<ؕ,eݣ4uwSwiÌINUt(p^lDZSƪŰp*eakaaնk]̕Vbn[tUꌪT:SL=e펲DhM>lJD%UTΧ)=DZғ~nǸU$s%ېV^&I+q̮|r+ĘNVH>l*$p6MADYn,bƩn0OzE:)&G9tj> O&<>x& \y^SK3|i[5ztB60:q3ܮ{ }K^To 5 vp ogr;dft5|eK$cҴ¤yGH}[]  8ZXX[z,:hBDUfq㺻埃ױ&E;lnJҢSn "3|jq^ 5m1KNktJb4w(WDOO>JNPE@#*:ڧ߿Py S|m|C >JKw= z: J7Ia/߰6dI^N= =SǤ5|P2~΀Ҧ]MbUf.V}&e\pd$cD{!!itonma]+ꨭ9Bu`V m]ȥ2k| jܳl2eW[U))Sg@{Hbxq"ӼdTQ:wyH+7 %@@xvKsل4Gݛ6G\o FjpmUڐ7GL9O)czB,FP5+DÐLvCVJYu ɍvwr&=v)S.s<44hT)D/aET6LP Zl 8<׌^э(GI#&ik&B䙡YBݘy~60_]hᑴ+iWc$BpU(}\7t`FxL^4vB~vhwuЫtyrv{|N^AOV[CFCa>ڪBce'5@DMG\(l67Rޤ!/ީmէ!~Kc5 qH2ڑ򭠁۵gBd{4n[OD]#igQ؄6Dtua$Ѫk> K1b ]!D܆e{B {2WFl  j5rU4u!mxRF )" `K=( ȍx^=;Q0_+P&9'=4= K4?^Fz]RAcəbn cNAzI>A^#!ƪ)i}hV'BAn hM,9C$c~s!CU>ISF"!"Y u*F a TQz oSDskQ0,d F`sCXsShEP.)gS)bԘX0:(Swe00fgSj!)_!YF|Z!FQvc~pҴ*ڶ! d,~)?Ar(a80cOL 6\qr}fr$qaDO7D#FavDz@ u4k y\ehYO _.^el~lI,,s)`CɆQ9b͗YyDC䔂t9`@.˛\@pQTEnʪLeR÷&81oO;JbNFh}.x.^ hLaUai1hsAh*H;u<+jdy䤩Oqp$anj <5QAވƘP>j2:tLJAI7yPcrO)\;,0K櫀tl;csXT[!k1cP9Ld S0ɟ؊T @ Le1$hx46ხLtUFYMTpgrXLS+-x|0e~YpP? Y#S,&=Z@$k {+$nNp~a>Pm[f.2rS/J>о"'=` {ƛ͠0m>:PMW.T7-}Ñ|}9TYVX9ř~/@ӆdS{W䌽܀=Q8&\.ֿNr(qV'7 PM=4PO62gV FmZ8߷ X;3f. ^hPOCdq^[5łk=X5)& H8J>#_)趫N瞑IBhwB~oüdvEɠ~Bny7n\#cR`6Zyyhtjv3c `<+cv.3?C;Bhq@m_I(p[%St3#&NѾշz%}3Sn ݚB6ɀCү\@\ՠhK|Lآ =='/z_x:f̺9 TV~uf_<(yݻ5;,kP}T޿/.OB?OKՊ {L2~:xch69黶㡡o'MR^PNԢPY1|'suǣ!{+2~ Kvu\d?v uHDK6ksYG;}WDea={<v=^_=c; 19_rh?(9l-X!.U4h.(,&G#,^#nWBrۧ/ o;UQ-삑\;~)H|qbP6כ5c-*~%i 9uqeh:~ D' k /P̾B@I?d^$UA7tv$ܵؐ9k3$ 5yHAjΘdSM>ZZMqD%Zu_fL69H6ՃW R1GN1JU"~ju!]6/M~;*2 5 XE3d"BP5m]rZk愡RΤu;_bA 8 gRo)8RWoYN=40 X'YjZ=?jIH^/CȜ9 d-LT*&4*2We1ة0|}.1 '|JP!agg2zZT|DH0WyK_A'dDxU!ki"Po0K15Y@ôJ)ޠة0ՅdJ0js°uR )`%14ilMz;>w:JH+[̫i~Ƒ[pUBB^ws "!axu5#޼zf+8̕ m:`TԨ4 XP<^k^[ ^gV7zVߜ1.Wf>e,_Ԩ;As-(G:,[Q$ы޲ׁE.cJ3(>(ONW&lX!5s :BZ Ae8%&Muu(oY6&lQ\rDL[n)-mx3v#V-B%$ MjMa ?"(WPȌtҗ / L_QEbH(4{_|EJ ',%ⱦMůXeMk$UPoqYjNuwP╧@WacZWjN!JP0VB,;i?pM%5Ð05û~]TeZ)z"HEX WJF1 fʊn11 uc僤P Yx^jxyRlaW1Z ?(]0U^T@+|`Rm%7=XQyFU'opI#qq5N %'PkTA#75aq^`q\CDTSpTq,Ks06HdJ <jJXu]@3֘3yQØhG֕3xftŎQ5ikU ~޹lqVK~iH E*UB[3#k@\DzIUgrC+|!ϏD,!Y +T(]! 7F 2mXyE9zr>.[gD bᓶE\=?CL1p*9j4NqDPx\ߣkk}jֵQ" juc|A-K7?\'_YxM 44?E0j4ARwR Rp*JueݕX3j"Й[~`]dՊ|WP 2/2 03WV{'l'BG?uYcQ+{׿;$ ?UѪ4%r5m:gWd_<αNIj6LXVNqU#V4Z_e5 f~͑<.OY, yX-_HoCo CwZW"~4"SVT|p~O)60%(2BHf+2y ur!R~WEp2<ç8!RbuTϥxSQO|N6GPaB_ ~U@ ^ȗ rA8?PWmrHġ."Xd#m|d,xh~uXPZ?lo"u;҈$dEnW<`k- 88SM^t"bbNE@]7QZ' Vuʇ!v-CwEjlX˦jE]X)Q]B ya\/)cR^r4$ Svah8a/05iuX1 .|Y6bGW!#UU ejQ@AN{ݮ( L[xIYy,Ee,ߌ^oHdȀ|0`A!*},K@P1 a!ex1-9C( ܅W> p7F"TJD9!pb^ߐn('5~:L2|`E|؜5/h=tWQ F{iA#gqZPh.N) k# $?rݳ:oT*!)W䟁T8Te\&^!d7?vfڒlXfy e}yN;@uf ;e9t.fA.g`r |C`$13)2& ϐQSTG񀝑0d$ tdg d7Ϧ&!8GT F 54$_3mC`'An }\dy] MRTX\}\rz_~YF/晄@d*C8)~a4ϐ_d%W7kL{>U "*:~ _S,BLX14@P?k*ǧ\VPB 2Ѡ|,b>7C-9*"JS|1Ul4䪔oȀNxtNh~as/ąC鮼Y UB͂QqAA {N_,~Wé.nf.G P= J!D3Q+C\P-DOVLo Q-8D6]i  O3\+~+ܒ/ص "{Sc0o/@ d@Tb +05Jt/ @ă;@g>3a)% 4 =/%yS]QR .1N mv29f 6e/u: F)HZ rKGG<1I3\b 4R֪.- ?wHyg`S"%^v^^,*px=ʩ~e(>ycQEQ GoJAD;q?r?S.[@E)Q)o6Օgbn$o@͇)_1Sa_f\HW'OpĤ<\vI~p51hJ91w{*9GM|$=i F|\D|];̑%^֮'  >&9p{Q=9SYq)Ϥ?WY 7ͫN?1emΜʃXd$4D|dXO&ONy*O fw oRF@OZ߹g@HZf,G08:mzȤ10o3DzƐVQ5?#]=B >cӂz f ~K2gpN^Wo~eݏ`Y(M '12ߢ<$9C_0yԌ4viW{o>*k$wlוcr+S\m#9 G*iمv.'ӿWSf!yu(utW\,_(`W[0]A| ?lb)B4ز*?);vkc{]}HȲE-m &$Kki u6`jnkq $ U!zHڠNGq 4m'6SX 6KDZHk {p6}CH䶏{tۇꖤ톾E|K_y6 rfWߟ{hբUrĿmn֓jOd}T\qntV}?qvlGƴ)N.ݤgO)g7o4$;ᚔP;)z+b|EɌY-~R{`*wKe'`gU֋xo=LX]矱v!b` =`_zIv#E1;CUpR626R tQo%/r~oXi; ?IX4+>YK%8'bqtc&U?u?9Yo=ڽUO`Abwv&]|Q$u+!WEa-M>d$6x[ZSu%wA+WRxSퟌ@^n?$ ]DLEt[.K/Im~0D ¹УQ*w1:$֒"x$.U~$.$hHQz&/4Q?g$.Ҩ]˖B{ Qߟ.?K+ץ~UvWD(xKf_j$To V%_#[ rإ~K+إwÊ%w]'d1qF:I@@v5>tPo*D.S*rY7]ٲoy_ҟ.Y?zoX^oõ~mPH@_=A7_)oׄhv-z.=ڡ_FFF!E"OMsHVߛ|6LD-]@zJ^ brMlHdbJÓϾ/8=u9ۙ 0`}s0>`v@]T nowyߞ0Pe_nWNDLM O1\ O 37g!T'콅=W+GL{]r, _'ͽӥ0 C5_Rv@n+2pԇ` lCow!նiI`\ }J{[!&6`l^r|NrKi>U|Y }5v4{uY˼SS4|{a[~;`way7o;&C@uIeIvX]Z5> u 8"x${U';-H|4?SzV:SN@*]sp4M2q VhJ4|N6!kAgFlh̩"E𱀛M0@tX\eJlx[? Hm˩^ǵ1o7ͪV/|V$zm|!ZrFpa#"X \㵋 |0sXbQ" חcZ7c/ȚŸ*GLHK8DtZ:! +7 5f(C*Yyg'6n.j[!bJ[/~L!"Olt1o1kaf0vgzb<* }: {]r D+&:V@Y7ȬI}ߦaQULC.O88TWSDT-B.cZZ h-4),QO%FGk(}"zJC 'ta `@Yy|py\%gPN.w4aOj{hg껰1ɷ_b.Ē/ThQT]I|%@%68UΉ.m bx rYF6mq1Хx|-b1hSS#jUwUP&8G^ љvF!w$%kޥYAD^&Q1 8 YpGVw/>1)$ j,m4=t`< rヒz;]&q8*u:|Rt% |=rD 0r,@Ǥ88a7}q2qƱJMUօhgpr؎HkL՗ً:/Ƥ!0ݷ. >Ca{DJbi4A#[atLc7{ıSEy*v|p m;Xdd-՚3vx'DL٣sh16cilQZ(-4F22kR9lハL.ȕAVdA"jĪbXT+ ujFꔜ_G`8(E_Cyҁn{]aӔ/L_ڻYCaU}W48>jƱ^܏HL1vmib0<2~Yf%S/pU @ @;fюhQ@kP^ugL`WO(McA \!`C$Խlx& Y,h2Ckr&FzLLj>:/z7 eY6#fjH&]w:fTi}VEytDKYAm7A͛*mAədwd~ߌԸc"kD_ʀ.N;>8{&wEԻ"ɕ[yKp6Lq$&o5>ie/i qLy804a*PC[68^\X86TiKWۏm{6G@(fΦD8&q8(kXYj,5DRҁiSq8h ;L_;`&~ ^ dͤ2Ȧ rpD J3 AIEDXobf$j!{mh^ ^z[pa'z`V*ѣW{F׫q&9 4 #<@ْ!˛GzH;p1h"jGXMaai5WDZy a;j^RJHyCM2+ E5QpC/tk ӬH|vbAZEsyg 3#-|\ (TL#!P&j&<QVr,H܋Я߄Ub]\ -ƅ&wMPpSX#yU)ycP TC%)#1^]uU36I$dJՈ-ߔ ʇ0[D>X%CXfT" z:喉/B]rLqї _ˣ5{g"dF&s82yAK]uLM8#\lDȠW8Xm("Fe3S-"PÉp/?,$8L~M E Hi\U܏jx hd,]qqx>;4@䘏 +I)? -NbpF;<4v#u6:h+;-Pdag&`nO8[(=^5H \i'WM@Ɂⵋ.KgLurq.2ZZrw >pw6|QkdH?eq4DL0yRǚ.JDhǬ͋e:~y!YfXrܭFߐI :ʙ0A=!yAeh%3?oiN+m<_n* ZNdFk/^N1+ذqY/ț%{KQq}PZSf"Si[VA3q (2G]2_^~`&&_1m\..t%&AW0Ũ7>"AxHCiQ\Ie-PRj8C`<걊&k#☑'Zڴ`˚*tPb3ijh2 # ݜUtmD]h c耐qC81]P`(?WX4]h~mԤс9EVL/V㨬*c_3 Vƚf3 XdTm܊\]t"%7stW9`'b*.l1XLG؞MBf(;Btϥ$ K,.ȅ}vU>qp4thN# vM0;څ8q! )3R ؍I(q"f$ 80uRLG ̍,K6`c8t]]ciT?rMe?ag9YH_flx`2͝Ǯy,H1e~hbp .]aws<ǸLp$;̧>b#T#9&L)~SW??~c`\L2|4Cz2fXL[5]SD!v+Q1vOp`E?NGBWˇCB^; TIս}^ f)Ab.6u/H܁,7eKzSUK8WL^~Qm{m3GZ!0RCt7n/ mDR!F˙]7ޓ;Lh{f=D=4}1E g8UqQ`ކ&&L)x\P_ Znb?rk2e[=9TO$z}3b|LŌBX9=Ѱ׻*|f 92mcpyqG?ԉdm3 =D)ܣSMC ~7N46{ia}#MQpng1pڟ WLJH׼l% qʉvEy2:)WFOk&A崭tc{ŕ8$j8tp$X&X 2#vW^^] fqfSAh d\a~I4߽}"HV$CLi]ǦV\`ugV45ͯ=`&K65XhuyھEqG(5@.3c\}]jw}ymYۦ,&yvv#%SQn0e| W'}Ԥмs ɽ:Anֺ%ia(tּ2:O];IDcnhxu.y@$I5X`njRZi8Kv5y=Ȥ)f]ߘSFË m#'4&0rm]m8蚒(kijWv8. TOy1 ygO{wӶ./gJWϮ6Ym#ukջ˄ww{{"W כ|[ݻVs/W;J3OIw?QSj黁)}Aco{ j;;`q~  HwSTK \nK=KC>lA*lWs% H' Y=Ƃ.0+L ,_Rg%sb黉Y`vRE^n| R&F;;pڇw#SN?` ^\hn;;a8 'WӊC!;K߭ 9> ӡl?\'־WxHCUcڶU3 >;u̎˽Xow! }]Z ;16ۘw HgR4^U /J! Z KQū/LaUK"l֊.^ۨy"U(Fhun,qkab D֍HLP!5XV5ZLU(bZyEs^Xz^%! ogDwv7HE>Z@JU?2u•Ep ,ig҂7Ìaث9k#\ #y)Ψ^ 3_X%yךɍSX${4 itJKDp5~P=t&(+?P/1AK'YZ-!B嘵 Ȁ*?Tw50LU醳d!&@(e r@nњUGLTIlR{ё7QN#hjC@|ksiF~uD]|BU sFvy @ͯU#KZWEy4Jlב'B!F'>mp/@!M%rLk- a0ceT^P]x0x"+ΠA ݗGyH;5UVA1j HJGU#+h4.}aD\ݤ hq~̆î4=_5C#Ic,uPXT ѯpC}Q7Mus2BM5*^"|o!uQ.K0Yl!v%2|&w[F 00CϨłCꪎzyqoX郩|硴=.Db(S4yR/_AqW~$"z<|g1 6^tm/Fz]z.r67Vj0`k8.KlENYmՋ/d|ii%Nʕ!M`e<DSB"׵(B0]AЯIselDijXV K00*/!ø)_MQV45ςD>T;СI7Q|yVej{kp?TFjAGU)^ǫ`~f&Yd{hW%dt,􃗤+fEuNHq^1yN ޵/y.7Q=Tr} MuCF|&\2RH4kMrQ@ũ06cQy.OjWpsX7FݼD:_oЁ\-{i+ 4t*8'qUodvd]=~<;衕\>wO\$yyG\\es~y`M+ YK]r:{DWD&K/Acդ`!sٔҲryTˑ/y]s''ZJnb.r)U4!9XP@s *п뀄@xģٰFYG e<gXF[lXqW}rBM&''T8J^$>ZA e˂*PyᡩQe>Qɇ*-53\/έ2ee6' ̔BֈIh])nz>ň%eQxW *b9/ _SjAO#PIWuHKO06-7z*Pm|T2r-)BZ@XpYkKHw{78{;YnM*{w.{` Vw+ߥΊwVL)Eqѻ-$~ջOUw7ww\>x?r&|g%- BV[Խp w-ubV o]z]:̏𫔢wERIC_[q;456)(sWA{Ż~9Gͻ) `+{wiΙ'w{7OrwO10gu;ĸBj;/c J"9NWKܬF?zp\R J,9@ N76J:IXlFoMUdX(c%:6&8nV&M lFq\΁/i7rww}@%pĺ#i0,|bٹFɹ<kAnguMeT@zi+@*gd4J0txRW=fx׷fX]oN1A \vS3%`o Vp9p0)Aq Hx׻H0 eHĴdp l]m@7^"(Wmg9H\5#'4o'j-ŋ49еt860;M8c/z$y u3~3;F|ɤ@?$o7.coQm7Ɓt R M\xm;>lW{'Eo.Q钉ցǵuK"OZn*MR~XP .Y9RdZ>Y]8I]$wI$M*a2?714ۻMqN\V@y&y38krji ulp!=']w)sXYO P(k`w8)KvͦB`':,Z[ -r#.=S4>u@pi ئ~ܧnY~`!pTc4 BMVQ3X_l[_D+kOw OWw` HXyz^w],6 WD5 80]w\dՆm `;^lht$n6ց?;pqށ iR V7Bzvxbk7W+?ڷftb*^n p =9`Ru<+ڳ:Xcqh}CNs>9) ρmsЧɾd k76FF%/d%lY ܓ$~IB!tVz7?l: Ҩޏ1#XD*PY톋}.!]X:[fA:k| ?=pvlowA~I΁C|?;nhrtClI?"<`!02M!@pa ; =Dqm"HA.EAw= nlmNG`&3N 6O$b;7I@ssWm춃AP($ޯP/kB7DzU_h Tρ?>8L&Ϩ#9`qT> ~crg O]Jg((”±{l!կ5 E?Y!mGϸ Xb@, lRS%ãKv:}ei~ihءF0mEa\LuGISb.65a.~Hcc06p¾ J^フN}|şƻŘ>>)~I>:JV+ gJu^r~ yf@ q^lѕYO)!WxE2a*IM1^>4њfѹG,Z&:AS"+hF,D *5uM``^1ה}YEy9;^l[1Z4:|br84hXUL8Ƥe ~ʥG;DӁ - -=`qh;ǸУ6pQvۏv(Ƕc,P9y|p|y4f'Pb.4.'uޜהo=~`=#f^PJ|Nxt+W|o@)$B)1r}g-ĸMGhHdD| 1 /BW qyb3Y{e>qq%,hE1P<>xG\j4Ґ?Y viEXov#Dw*kpY{Cڿ]\@>FՐh?=[}6܎4"w|pͻaɳZMyecaɰԻ(v8A×e~If+;Թњ:tOv M7 ߂eSoSea|gqpoڀw&Md[$6$;nG2Ri+Xvj>2 9{(lp.Fl*6+/11@1j u}Ap &R^ --^ DeruOׂ[pXAYԲD%b XЫq ҵpl5Vy;J_\6+dh*V yQ0&ﲲhFmǶYv59]!d߆faF1Lטi1eWc=.=R !$tb~a4&sǘۂOXt^iphoφϨ}`c4w>ї . Ec =~ae)T Rښ:^9Jv2+ /4<>&y7t๵4P*F >pRv*AtH2(!7`+LGm.wf Ǎ7$Ɲ 5{H1?+=61:IaGzHq zcPI4169j1X[hF8zliGZ-ek \0h*3W >_&E1RF:Nq;:(j" fakDΫUG #oDECVnb@cΨ_U-Jjס:e:h-[pTG==2O$Q~KX*VB}nBuF@QzVt9"(էҗgg_ˉ靮kċ A && aό.BZ4֤p4TbW*r655P,0 eiWFdP*/`CF*7&ψ[|%S!Kdy3#x.&C>$b>hHiJaH/LMb{)W թx&Û\L~g\Ih͏X^]eObrq ☥J1:Jnbˡևf!by"jZZfP_Ti4l!CU>ISF$ "Y (?↻ {< =7ݡ kAui'Sw0FBurbQ6 i k -CuQªon1C~$$?_nk}>알xN9+_͙M׃NUFMv&7j\ OL\Ir/hcDbQ q`D k)Ֆ1M4MFqaoS^K:&7R3Pͳ?@.tj;Z*ңR|q a4MAW6g6>Xۄ[qYףrGaD r1})@o±Hs@#tFpX50{ˏ'%Ohưq܎3eeBUwg֌Y|S!ԿȽIM>QHkqhlfc˶z*7ӟ !?zætV RʣDhب/0"m'\Qd2^\4FRVt#6LׅtsF|C8Nmg{E "ˌ g00R}C$osL9uݹr>Fм o}h3r?UP^fo(˔y0dо[lC@WBs,vR} _ȭ)^IGL&MPdjva^lT2HĀ\9vm3{Cz:fzj@5{/&/Iݚ} CNuIˎbLե8rGB:xD=S]{aCYЙSk+_X7 %tP{&b8zuv9[F3]`} ުTkQWUIcװ\ޏ[}y[1mSG ")+3bAhV2_T^~) 9O+(Ty0.0$>8lw,vzR{ GB(n#Yuuvi/Xaӗ7\`}` `$$FH=ESg'YmsYC0f|OկKN=0lA 2ӽPH%:v1%/pk 둢/t}NfߨU:oB_~&PVH=@sB(g i%"4(N~Hd[ԸDRl#„Bѧ!'f+(."bE&#P Z~W+bߊm~f #B,-jQeKTz-FTx0<ΑUT|^G%D! 6F^c{Vy41SQ.暂a #TGÐ6KOG C x*m\<"$ &SLayt%vaA9K6OD\MU 5O<IOw*w:#V#u[ e:K}gAv 0mR%eR R6U4R+ZAgH@4;ggf:oJ V+R$^I/|5_i@[ OZDg$}X ds#DL Q,`ʯ%DxcQF41#ZDNёU#V4Qdbi^}͑'vv<BАh=h7AFYW`G$‹畈H딕u*N2U{*eAh&ʽu3>HcN..EUF\Cr"RhnVw'b9P2#!VG]Lka\*ב:SB ,~7] S!~QˬhA3vR,qˠe!*G*z2zfѳ d-H~cA,8wu439D2Q`AM ADy\1y"^7GGav<}aۄLx7yэͥGK:UbHv(~\bZDڰ-~r31j7En Q:|:,tA!L)Zc<}YI~s%!Ki/F(+qdctb_+i Lѡ}ߤ{lUGE2WWeTƊ  ArNR/:g (/O IK2-Qa Z6)J0 ^9sK Cj##yDK91=:cheyeDmW@%e9Fddcl~4Y p)e_Ouy#K]9f0,k*B@{W[[X7_xqBFD)#>Av5=f*p $1^XRy"/r&̨z 8`F $2g9r~9(ms$S`F&AFъy?CZ, zGAS"b^洠Y;ŻSܘWXXd_obF3 嬒ʅR'dQgHq+Dćc0 y(sZDґ#Tr9#%`G<*$$h3n|Nxd}܉x_W~NsY]bRZQj J-GtߩZYأ8>(a\H ;a*ۇ4,U3>{QOГ dh]$'An6X͞?&]Z4XjC{?-%?#̆-=į$]¸+?p҄sP靪CPUq7)]?iA~N/w~R?Mwii7?| @C0I~t0`a*>"1oag Oz,"bgj_vG3,,Yb"lO͵$) 7#I>Va3w*tE]IGߥ܈!)ʟ|Lb^C_uUyj >} }v]OGUWPP˽FOnDIhJ߮5 '*N'ԫBq_nݢE(C:-?#^̮TuET'Xwcݍ$04(` ԱZ3)f?U;[u׺u_ҪuE݊ܿp]z]WI%Y+N }? }] C:$pzoj]~)UWjw͖Ms|25wS_˷eah$0A38HM9oiC N *X]LTHO35,E;=eG?MX^%02o\?(A4-pvWFkk)ayޥ :ġm] wZJ$۵;qD>efO߫٥;)ZR!?|dJguԙH?`.N!I6=/0 rJ|N%QHOش 95h$>pI,L]Ksoj$& ow:}YS$lIIo^[eH//l- )=0|JlWe>ȄZ5OL%6E oLɊYlivLgu4Mu;<Ԧ,?>SdH{"Z'p Z3I=1nTB Ɋls^UfPe7{jGRr;<m>wij9CF:fyM&njSSk?YƼT3NW]Z`yOɺ'ߒ1-9 V.~KKE{E$4GZ^S(hq"]n[9ǀuZ#`uIx&AH΢:&ТQ5kvk24mh?o0  S v&Ѯ\6;ԖXvJ%< >ء.m司Kn[N@PqCȦuie'+ 7Nj9ZD #include #include #include #include #define KEY_TIMEOUT (KEY_MAX + 1) void dpy_init(const char* argv[]) { } void dpy_start(void) { initscr(); raw(); noecho(); meta(NULL, TRUE); nonl(); idlok(stdscr, TRUE); idcok(stdscr, TRUE); scrollok(stdscr, FALSE); intrflush(stdscr, FALSE); //notimeout(stdscr, TRUE); keypad(stdscr, TRUE); } void dpy_shutdown(void) { endwin(); } void dpy_clearscreen(void) { erase(); } void dpy_getscreensize(int* x, int* y) { getmaxyx(stdscr, *y, *x); } void dpy_sync(void) { refresh(); } void dpy_setcursor(int x, int y) { move(y, x); } void dpy_setattr(int andmask, int ormask) { static int attr = 0; attr &= andmask; attr |= ormask; int cattr = 0; if (attr & (DPY_ITALIC|DPY_BOLD|DPY_BRIGHT)) cattr |= A_BOLD; if (attr & DPY_DIM) cattr |= A_DIM; if (attr & DPY_UNDERLINE) cattr |= A_UNDERLINE; if (attr & DPY_REVERSE) cattr |= A_REVERSE; attrset(cattr); } void dpy_writechar(int x, int y, uni_t c) { wchar_t cc = c; mvaddnwstr(y, x, &cc, 1); } void dpy_cleararea(int x1, int y1, int x2, int y2) { wchar_t cc = ' '; for (int y = y1; y <= y2; y++) for (int x = x1; x <= x2; x++) mvaddnwstr(y, x, &cc, 1); } uni_t dpy_getchar(int timeout) { struct timeval then; gettimeofday(&then, NULL); u_int64_t thenms = (then.tv_usec/1000) + ((u_int64_t) then.tv_sec*1000); for (;;) { if (timeout != -1) { struct timeval now; gettimeofday(&now, NULL); u_int64_t nowms = (now.tv_usec/1000) + ((u_int64_t) now.tv_sec*1000); int delay = ((u_int64_t) timeout*1000) + nowms - thenms; if (delay <= 0) return -KEY_TIMEOUT; timeout(delay); } else timeout(-1); wint_t c; int r = get_wch(&c); if (r == ERR) /* timeout */ return -KEY_TIMEOUT; if ((r == KEY_CODE_YES) || !iswprint(c)) /* function key */ return -c; if (emu_wcwidth(c) > 0) return c; } } const char* dpy_getkeyname(uni_t k) { k = -k; switch (k) { case KEY_TIMEOUT: return "KEY_TIMEOUT"; case KEY_DOWN: return "KEY_DOWN"; case KEY_UP: return "KEY_UP"; case KEY_LEFT: return "KEY_LEFT"; case KEY_RIGHT: return "KEY_RIGHT"; case KEY_HOME: return "KEY_HOME"; case KEY_END: return "KEY_END"; case KEY_BACKSPACE: return "KEY_BACKSPACE"; case KEY_DC: return "KEY_DELETE"; case KEY_IC: return "KEY_INSERT"; case KEY_NPAGE: return "KEY_PGDN"; case KEY_PPAGE: return "KEY_PGUP"; case KEY_STAB: return "KEY_STAB"; case KEY_CTAB: return "KEY_CTAB"; case KEY_CATAB: return "KEY_CATAB"; case KEY_ENTER: return "KEY_RETURN"; case KEY_SIC: return "KEY_SINSERT"; case KEY_SDC: return "KEY_SDELETE"; case KEY_SHOME: return "KEY_SHOME"; case KEY_SEND: return "KEY_SEND"; case KEY_SR: return "KEY_SUP"; case KEY_SF: return "KEY_SDOWN"; case KEY_SLEFT: return "KEY_SLEFT"; case KEY_SRIGHT: return "KEY_SRIGHT"; case KEY_MOUSE: return "KEY_MOUSE"; case KEY_RESIZE: return "KEY_RESIZE"; case KEY_EVENT: return "KEY_EVENT"; case 13: return "KEY_RETURN"; case 27: return "KEY_ESCAPE"; } static char buffer[32]; if (k < 32) { sprintf(buffer, "KEY_^%c", k+'A'-1); return buffer; } if ((k >= KEY_F0) && (k < (KEY_F0+64))) { sprintf(buffer, "KEY_F%d", k - KEY_F0); return buffer; } const char* name = keyname(k); if (name) { if (strcmp(name, "kUP5") == 0) return "KEY_^UP"; if (strcmp(name, "kRIT5") == 0) return "KEY_^RIGHT"; if (strcmp(name, "kDN5") == 0) return "KEY_^DOWN"; if (strcmp(name, "kLFT5") == 0) return "KEY_^LEFT"; if (strcmp(name, "kUP6") == 0) return "KEY_^SUP"; if (strcmp(name, "kRIT6") == 0) return "KEY_^SRIGHT"; if (strcmp(name, "kDN6") == 0) return "KEY_^SDOWN"; if (strcmp(name, "kLFT6") == 0) return "KEY_^SLEFT"; } sprintf(buffer, "KEY_UNKNOWN_%d", k); return buffer; } wordgrinder-0.5.1.orig/src/c/arch/win32/0000755000000000000000000000000012251160511014552 5ustar wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/0000755000000000000000000000000012251160511015315 5ustar wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/dpy.c0000644000000000000000000004277612247661723016315 0ustar /* © 2010 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. * * $Id: dpy.c 159 2009-12-13 13:11:03Z dtrg $ * $URL: https://wordgrinder.svn.sf.net/svnroot/wordgrinder/wordgrinder/src/c/arch/win32/console/dpy.c $ */ #include "globals.h" #include #include #include #include "gdi.h" #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) #define DEFAULT_CHAR (' ' << 8) #define MENUITEM_SETFONT 1 #define MENUITEM_FULLSCREEN 2 #define REGISTRY_PATH "Software\\Cowlark Technologies\\WordGrinder" HWND window = INVALID_HANDLE_VALUE; static LOGFONT fontlf; static unsigned int* frontbuffer = NULL; static unsigned int* backbuffer = NULL; static int screenwidth = 0; static int screenheight = 0; static int defaultattr = 0; static UINT_PTR timer = 0; static bool cursor_visible = false; static int cursorx = 0; static int cursory = 0; static bool isfullscreen = false; static bool window_geometry_valid = false; static RECT window_geometry; static bool window_created = false; static void resize_buffer(void); static void fullscreen_cb(void); static void switch_to_full_screen(void); static void switch_to_windowed(void); static void reset_cursor(void); static HKEY make_key(const char* keystring) { HKEY key; int e = RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); if (e == ERROR_SUCCESS) return key; return NULL; } static void read_default_font(void) { HKEY key = make_key(REGISTRY_PATH); DWORD size = sizeof(fontlf); DWORD e = RegQueryValueEx(key, "DefaultFont", NULL, NULL, (LPBYTE) &fontlf, &size); RegCloseKey(key); if (e != ERROR_SUCCESS) { HFONT defaultfont = (HFONT) GetStockObject(SYSTEM_FIXED_FONT); GetObject(defaultfont, sizeof(fontlf), &fontlf); } } static void write_default_font(void) { HKEY key = make_key(REGISTRY_PATH); RegSetValueEx(key, "DefaultFont", 0, REG_BINARY, (LPBYTE) &fontlf, sizeof(fontlf)); RegCloseKey(key); } static void read_window_geometry(void) { HKEY key = make_key(REGISTRY_PATH); DWORD size = sizeof(window_geometry); DWORD e = RegQueryValueEx(key, "WindowGeometry", NULL, NULL, (LPBYTE) &window_geometry, &size); RegCloseKey(key); if (e != ERROR_SUCCESS) window_geometry_valid = false; else window_geometry_valid = true; } static void write_window_geometry(void) { HKEY key = make_key(REGISTRY_PATH); if (window_geometry_valid) RegSetValueEx(key, "WindowGeometry", 0, REG_BINARY, (LPBYTE) &window_geometry, sizeof(window_geometry)); else RegDeleteKey(key, "WindowGeometry"); RegCloseKey(key); } static void unicode_key(uni_t key, unsigned flags) { if ((key >= 1) && (key <= 31)) { dpy_queuekey(-(VKM_CTRLASCII | key)); return; } if ((key == ' ') && (GetKeyState(VK_CONTROL) & 0x8000)) { dpy_queuekey(-(VKM_CTRLASCII | 0)); return; } dpy_queuekey(key); } static bool special_key(int vk, unsigned flags) { switch (vk) { case VK_RETURN: if (flags & (1<<29)) { fullscreen_cb(); return true; } break; case VK_SHIFT: case VK_CONTROL: case VK_CAPITAL: case VK_MENU: case VK_LWIN: case VK_RWIN: case VK_SNAPSHOT: case VK_PAUSE: return false; } if (vk > 0x90) return false; /* Numeric keypad. */ if ((vk >= 0x60) && (vk <= 0x6f)) return false; if ((vk == ' ') || isdigit(vk) || isupper(vk)) { if (flags & (1<<29)) { /* ALT pressed */ dpy_queuekey(-27); dpy_queuekey(vk); return true; } return false; } if (GetKeyState(VK_CONTROL) & 0x8000) vk |= VKM_CTRL; if (GetKeyState(VK_SHIFT) & 0x8000) vk |= VKM_SHIFT; dpy_queuekey(-vk); return true; } static void paint_cb(HWND window, PAINTSTRUCT* ps, HDC dc) { int textwidth, textheight; glyphcache_getfontsize(&textwidth, &textheight); struct glyph* cursorglyph = NULL; int x1 = ps->rcPaint.left / textwidth; x1 -= 1; /* because of overlapping characters */ if (x1 < 0) x1 = 0; int y1 = ps->rcPaint.top/textheight; int x2 = ps->rcPaint.right/textwidth; x2 += 1; /* because of overlapping characters */ if (x2 >= screenwidth) { RECT r = {screenwidth*textwidth, 0, ps->rcPaint.right, ps->rcPaint.bottom}; FillRect(dc, &r, GetStockObject(BLACK_BRUSH)); x2 = screenwidth; } int y2 = ps->rcPaint.bottom / textheight; if (y2 >= screenheight) { RECT r = {0, screenheight*textheight, ps->rcPaint.right, ps->rcPaint.bottom}; FillRect(dc, &r, GetStockObject(BLACK_BRUSH)); y2 = screenheight-1; } int state = SaveDC(dc); HPEN brightpen = CreatePen(PS_SOLID, 0, 0xffffff); HPEN normalpen = CreatePen(PS_SOLID, 0, 0x888888); HPEN dimpen = CreatePen(PS_SOLID, 0, 0x555555); for (int y = y1; y <= y2; y++) { int sy = y * textheight; /* Clear this line (or at least the part of it we're drawing). */ RECT r = {ps->rcPaint.left, sy, ps->rcPaint.right, sy+textheight}; FillRect(dc, &r, GetStockObject(BLACK_BRUSH)); /* Draw the actual text. */ for (int x = x1; x < x2; x++) { int seq = y*screenwidth + x; int sx = x * textwidth; unsigned int id = frontbuffer[seq]; struct glyph* glyph = glyphcache_getglyph(id, dc); if (glyph) { BitBlt(dc, sx+glyph->xoffset, sy+glyph->yoffset, glyph->realwidth, glyph->realheight, glyph->dc, 0, 0, SRCPAINT); if (id & DPY_UNDERLINE) { if (id & DPY_BRIGHT) SelectObject(dc, brightpen); else if (id & DPY_DIM) SelectObject(dc, dimpen); else SelectObject(dc, normalpen); MoveToEx(dc, sx, sy+textheight-1, NULL); LineTo(dc, sx+glyph->width, sy+textheight-1); } } if ((x == cursorx) && (y == cursory)) cursorglyph = glyph; } /* Now go through and invert any characters which are in reverse. */ for (int x = x1; x < x2; x++) { int seq = y*screenwidth + x; int sx = x * textwidth; unsigned int id = frontbuffer[seq]; if (id & DPY_REVERSE) { int w; struct glyph* glyph = glyphcache_getglyph(id, dc); if (glyph) w = glyph->width; else w = textwidth; BitBlt(dc, sx, sy, w, textheight, NULL, 0, 0, DSTINVERT); } } } /* Invert the square containing the cursor. */ if (cursor_visible && cursorglyph) BitBlt(dc, cursorx*textwidth, cursory*textheight, cursorglyph->width, textheight, NULL, 0, 0, DSTINVERT); DeleteObject(brightpen); DeleteObject(normalpen); DeleteObject(dimpen); RestoreDC(dc, state); } static void setfont_cb(void) { CHOOSEFONT cf; memset(&cf, 0, sizeof(cf)); cf.lStructSize = sizeof(cf); cf.hwndOwner = window; cf.lpLogFont = &fontlf; cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_FIXEDPITCHONLY | CF_SCREENFONTS; cf.nFontType = SCREEN_FONTTYPE; if (ChooseFont(&cf)) { write_default_font(); HDC dc = GetDC(window); glyphcache_deinit(); glyphcache_init(dc, &fontlf); ReleaseDC(window, dc); resize_buffer(); } } static void fullscreen_cb(void) { if (!isfullscreen) { window_geometry_valid = true; GetWindowRect(window, &window_geometry); write_window_geometry(); } isfullscreen = !isfullscreen; if (isfullscreen) switch_to_full_screen(); else switch_to_windowed(); resize_buffer(); } static void create_cb(void) { /* Initialise the glyph cache. */ read_default_font(); HDC dc = GetDC(window); glyphcache_init(dc, &fontlf); ReleaseDC(window, dc); } static void sizing_cb(int type, RECT* r) { int w = r->right - r->left; if (w < 100) w = 100; int h = r->bottom - r->top; if (h < 100) h = 100; switch (type) { case WMSZ_LEFT: case WMSZ_TOPLEFT: case WMSZ_BOTTOMLEFT: r->left = r->right - w; break; case WMSZ_RIGHT: case WMSZ_TOPRIGHT: case WMSZ_BOTTOMRIGHT: r->right = r->left + w; break; } switch (type) { case WMSZ_TOP: case WMSZ_TOPLEFT: case WMSZ_TOPRIGHT: r->top = r->bottom - h; break; case WMSZ_BOTTOM: case WMSZ_BOTTOMLEFT: case WMSZ_BOTTOMRIGHT: r->bottom = r->top + h; break; } } static LRESULT CALLBACK window_cb(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { dpy_flushkeys(); switch (message) { case WM_CLOSE: return 0; case WM_EXITSIZEMOVE: if (!isfullscreen) { window_geometry_valid = true; GetWindowRect(window, &window_geometry); write_window_geometry(); } break; case WM_SIZE: if (!window_created) { create_cb(); window_created = true; } resize_buffer(); break; case WM_SIZING: sizing_cb(wparam, (RECT*) lparam); goto delegate; case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(window, &ps); paint_cb(window, &ps, ps.hdc); EndPaint(window, &ps); break; } case WM_PRINTCLIENT: { PAINTSTRUCT ps; ps.hdc = (HDC) wparam; GetClientRect(window, &ps.rcPaint); paint_cb(window, &ps, ps.hdc); break; } case WM_CHAR: { reset_cursor(); unicode_key(wparam, lparam); break; } case WM_KEYDOWN: case WM_SYSKEYDOWN: { reset_cursor(); if (special_key(wparam, lparam)) return 1; break; } case WM_SYSCOMMAND: { switch (wparam) { case MENUITEM_SETFONT: setfont_cb(); break; case MENUITEM_FULLSCREEN: fullscreen_cb(); break; } goto delegate; } case WM_TIMER: { if (wparam == TIMEOUT_TIMER_ID) { dpy_queuekey(-VK_TIMEOUT); break; } goto delegate; } delegate: default: return DefWindowProcW(window, message, wparam, lparam); } return 0; } void dpy_init(const char* argv[]) { SystemParametersInfo(SPI_SETFONTSMOOTHING, TRUE, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETFONTSMOOTHINGTYPE, 0, (PVOID)FE_FONTSMOOTHINGCLEARTYPE, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); read_window_geometry(); } static void resize_buffer(void) { RECT rect; int e = GetClientRect(window, &rect); if (!e) SystemParametersInfo(SPI_GETWORKAREA, sizeof(RECT), &rect, 0); int textwidth, textheight; glyphcache_getfontsize(&textwidth, &textheight); int w = rect.right / textwidth; int h = rect.bottom / textheight; if ((w != screenwidth) || (h != screenheight)) { glyphcache_flush(); /* Wipe the character storage. */ screenwidth = w; screenheight = h; frontbuffer = realloc(frontbuffer, sizeof(unsigned int) * w * h); backbuffer = realloc(backbuffer, sizeof(unsigned int) * w * h); for (int p = 0; p < (w * h); p++) { frontbuffer[p] = 0; backbuffer[p] = DEFAULT_CHAR; } /* Tell the main app that the screen has changed size; it'll * redraw the character storage. */ dpy_queuekey(-VK_RESIZE); } /* The front end will redraw the content area, if necessary, but it * doesn't know anything about the screen borders. We need to force * them to be redrawn as well. */ RECT r; r = rect; r.left = w * textwidth; InvalidateRect(window, &r, 0); r = rect; r.top = h * textheight; InvalidateRect(window, &r, 0); } static void switch_to_full_screen(void) { HMONITOR monitor; if (window) { monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST); DestroyWindow(window); } else monitor = MonitorFromWindow(HWND_DESKTOP, MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof(mi); GetMonitorInfo(monitor, &mi); window = CreateWindowExW( WS_EX_TOPMOST, /* Extended class style */ L"WordGrinder", /* Class Name */ L"WordGrinder", /* Title */ WS_POPUP, /* Style */ mi.rcMonitor.left, /* x */ mi.rcMonitor.top, /* y */ mi.rcMonitor.right - mi.rcMonitor.left, /* width */ mi.rcMonitor.bottom - mi.rcMonitor.top, /* height */ HWND_DESKTOP, /* Parent */ NULL, /* No menu */ GetModuleHandle(NULL), /* Instance */ 0); /* No special parameters */ ShowWindow(window, SW_SHOWDEFAULT); } static void invalidate_character_at(int x, int y) { int textwidth, textheight; glyphcache_getfontsize(&textwidth, &textheight); RECT r; r.left = cursorx * textwidth; r.top = cursory * textheight; r.right = r.left + textwidth; r.bottom = r.top + textheight; InvalidateRect(window, &r, 0); } static VOID CALLBACK cursor_blink_cb(HWND hwnd, UINT message, UINT_PTR timer, DWORD time) { cursor_visible = !cursor_visible; invalidate_character_at(cursorx, cursory); UpdateWindow(window); } static void reset_cursor(void) { timer = SetTimer(NULL, timer, 500, cursor_blink_cb); cursor_visible = true; } static void switch_to_windowed(void) { if (window) DestroyWindow(window); /* Create the window. */ window = CreateWindowW( L"WordGrinder", /* Class Name */ L"WordGrinder", /* Title */ WS_OVERLAPPEDWINDOW, /* Style */ CW_USEDEFAULT, CW_USEDEFAULT, /* Position */ CW_USEDEFAULT, CW_USEDEFAULT, /* Size */ NULL, /* Parent */ NULL, /* No menu */ GetModuleHandle(NULL), /* Instance */ 0); /* No special parameters */ if (window_geometry_valid) SetWindowPos(window, HWND_TOP, window_geometry.left, window_geometry.top, window_geometry.right - window_geometry.left, window_geometry.bottom - window_geometry.top, SWP_NOZORDER); /* Add the window menu commands and disable the close button. */ { HMENU menu = GetSystemMenu(window, FALSE); int count = GetMenuItemCount(menu); MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_FTYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(menu, count++, TRUE, &mii); mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID; mii.fType = MFT_STRING; mii.dwTypeData = "Select display fon&t..."; mii.cch = strlen(mii.dwTypeData); mii.wID = MENUITEM_SETFONT; InsertMenuItem(menu, count++, TRUE, &mii); mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID; mii.fType = MFT_STRING; mii.dwTypeData = "&Fullscreen mode\tAlt+Enter"; mii.cch = strlen(mii.dwTypeData); mii.wID = MENUITEM_FULLSCREEN; InsertMenuItem(menu, count++, TRUE, &mii); EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } ShowWindow(window, SW_SHOWDEFAULT); } void dpy_start(void) { /* Register our window class. */ { WNDCLASSW wc; wc.style = CS_OWNDC; wc.lpfnWndProc = window_cb; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = GetModuleHandle(NULL); wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(101)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = L"WordGrinder"; if (!RegisterClassW(&wc)) { fprintf(stderr, "Unable to register window class.\n"); exit(-1); } } reset_cursor(); switch_to_windowed(); } void dpy_shutdown(void) { free(frontbuffer); free(backbuffer); glyphcache_deinit(); } void dpy_clearscreen(void) { dpy_cleararea(0, 0, screenwidth-1, screenheight-1); } void dpy_getscreensize(int* x, int* y) { *x = screenwidth; *y = screenheight; } void dpy_sync(void) { int textwidth, textheight; glyphcache_getfontsize(&textwidth, &textheight); for (int y=0; y= screenwidth) || (y >= screenheight)) return; backbuffer[y*screenwidth + x] = (c<<8) | defaultattr; } void dpy_cleararea(int x1, int y1, int x2, int y2) { for (int y = y1; y <= y2; y++) for (int x = x1; x <= x2; x++) backbuffer[y*screenwidth + x] = (' '<<8) | defaultattr; } const char* dpy_getkeyname(uni_t k) { switch (-k) { case VK_RESIZE: return "KEY_RESIZE"; case VK_TIMEOUT: return "KEY_TIMEOUT"; case VK_REDRAW: return "KEY_REDRAW"; } int mods = -k; int key = (-k & 0xFF); static char buffer[32]; if (mods & VKM_CTRLASCII) { sprintf(buffer, "KEY_%s^%c", (mods & VKM_SHIFT) ? "S" : "", key + 64); return buffer; } const char* template = NULL; switch (key) { case VK_NUMLOCK: return NULL; case VK_DOWN: template = "DOWN"; break; case VK_UP: template = "UP"; break; case VK_LEFT: template = "LEFT"; break; case VK_RIGHT: template = "RIGHT"; break; case VK_HOME: template = "HOME"; break; case VK_END: template = "END"; break; case VK_BACK: template = "BACKSPACE"; break; case VK_DELETE: template = "DELETE"; break; case VK_INSERT: template = "INSERT"; break; case VK_NEXT: template = "PGDN"; break; case VK_PRIOR: template = "PGUP"; break; case VK_TAB: template = "TAB"; break; case VK_RETURN: template = "RETURN"; break; case VK_ESCAPE: template = "ESCAPE"; break; } if (template) { sprintf(buffer, "KEY_%s%s%s", (mods & VKM_SHIFT) ? "S" : "", (mods & VKM_CTRL) ? "^" : "", template); return buffer; } if ((key >= VK_F1) && (key <= (VK_F24))) { sprintf(buffer, "KEY_%s%sF%d", (mods & VKM_SHIFT) ? "S" : "", (mods & VKM_CTRL) ? "^" : "", key - VK_F1 + 1); return buffer; } sprintf(buffer, "KEY_UNKNOWN_%d", -k); return buffer; } wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/gdi.h0000644000000000000000000000245212243251035016237 0ustar /* © 2010 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. * * $Id: dpy.c 159 2009-12-13 13:11:03Z dtrg $ * $URL: https://wordgrinder.svn.sf.net/svnroot/wordgrinder/wordgrinder/src/c/arch/win32/console/dpy.c $ */ #ifndef GDI_H #define GDI_H #include "uthash.h" #define VKM_SHIFT 0x100 #define VKM_CTRL 0x200 #define VKM_CTRLASCII 0x400 #define VK_RESIZE 0x1000 #define VK_TIMEOUT 0x1001 #define VK_REDRAW 0x1002 #define TIMEOUT_TIMER_ID 1 struct glyph { unsigned int id; /* id of this glyph */ HDC dc; /* Memory DC this glyph is drawn to */ HBITMAP bitmap; /* Backing store for memory DC */ int width; /* Width of cell for this glyph */ int xoffset, yoffset; /* Draw offset */ int realwidth, realheight; /* Actual size of glyph bitmap */ UT_hash_handle hh; }; extern void glyphcache_init(HDC dc, LOGFONT* defaultfont); extern void glyphcache_deinit(void); extern void glyphcache_getfontsize(int* w, int* h); extern void glyphcache_flush(void); extern struct glyph* glyphcache_getglyph(unsigned int id, HDC dc); extern void dpy_queuekey(uni_t key); extern void dpy_flushkeys(void); extern HWND window; #endif wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/glyphcache.c0000644000000000000000000003124512246725644017617 0ustar /* © 2010 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. * * $Id: dpy.c 159 2009-12-13 13:11:03Z dtrg $ * $URL: https://wordgrinder.svn.sf.net/svnroot/wordgrinder/wordgrinder/src/c/arch/win32/console/dpy.c $ */ #include "globals.h" #include #include #include "gdi.h" static struct glyph* glyphs; static int fontwidth = 0; static int fontheight = 0; struct fontinfo { LOGFONT logfont; long long panose; HFONT font; HFONT fontb; HFONT fonti; HFONT fontbi; GLYPHSET* glyphset; bool defaultfont : 1; }; static int numfonts = 0; static struct fontinfo* fontdata = NULL; #define DEFAULT_PANOSE 0x2b66900000LL static struct glyph* create_struct_glyph(void) { return calloc(1, sizeof(struct glyph)); } static void delete_struct_glyph(struct glyph* glyph) { if (!glyph) return; if (glyph->bitmap) DeleteObject(glyph->bitmap); if (glyph->dc) DeleteDC(glyph->dc); free(glyph); } static int CALLBACK font_counter_cb( ENUMLOGFONTEX* fontex, NEWTEXTMETRICEX* metrics, DWORD type, LPARAM user) { numfonts++; return 1; } struct font_reader_cb_data { int index; HDC dc; long long currentpanose; }; static long long compare_panose(unsigned long long p1, unsigned long long p2) { unsigned long long result = 0; for (int i=60; i >= 0; i--) { int d1 = (p1 >> i) & 0xf; int d2 = (p2 >> i) & 0xf; if ((d1 != 0) && (d1 != 1) && (d2 != 0) && (d2 != 1)) { int d = d1 - d2; if (d < 0) d = -d; result |= d; } result <<= 4; } return result; } static long long decode_panose(PANOSE* panose) { BYTE* panosebytes = (BYTE*) panose; long long number = 0; for (int i = 0; i < PANOSE_COUNT; i++) number = (number<<4) | panosebytes[i]; return number; } static long long get_panose(HFONT font, HDC dc) { SelectObject(dc, font); int result = GetOutlineTextMetrics(dc, 0, NULL); if (result) { char buffer[result]; LPOUTLINETEXTMETRIC metrics = (void*) buffer; GetOutlineTextMetrics(dc, result, metrics); return decode_panose(&metrics->otmPanoseNumber); } else return DEFAULT_PANOSE; } static int CALLBACK font_reader_cb( ENUMLOGFONTEX* fontex, NEWTEXTMETRICEX* metrics, DWORD type, LPARAM user) { struct font_reader_cb_data* data = (void*) user; struct fontinfo* fi = &fontdata[data->index]; fi->logfont = fontex->elfLogFont; fi->logfont.lfWidth = 0;//fontwidth; fi->logfont.lfHeight = fontheight; fi->logfont.lfItalic = FALSE; fi->logfont.lfWeight = FW_NORMAL; fi->font = CreateFontIndirect(&fi->logfont); fi->logfont.lfItalic = TRUE; fi->logfont.lfWeight = FW_NORMAL; fi->fonti = CreateFontIndirect(&fi->logfont); fi->logfont.lfItalic = FALSE; fi->logfont.lfWeight = FW_BOLD; fi->fontb = CreateFontIndirect(&fi->logfont); fi->logfont.lfItalic = TRUE; fi->logfont.lfWeight = FW_BOLD; fi->fontbi = CreateFontIndirect(&fi->logfont); unsigned long long p = get_panose(fi->font, data->dc); fi->panose = compare_panose(p, data->currentpanose); SelectObject(data->dc, fi->font); int glyphsetsize = GetFontUnicodeRanges(data->dc, NULL); fi->glyphset = malloc(glyphsetsize); assert(fi->glyphset); GetFontUnicodeRanges(data->dc, fi->glyphset); fi->defaultfont = false; data->index++; return 1; } static int font_sorter_cb(const void* p1, const void* p2) { const struct fontinfo* f1 = p1; const struct fontinfo* f2 = p2; if (f1->defaultfont && !f2->defaultfont) return -1; if (!f1->defaultfont && f2->defaultfont) return 1; long long d = f1->panose - f2->panose; if (d == 0) return 0; if (d < 0) return -1; return 1; } void glyphcache_init(HDC dc, LOGFONT* defaultfont) { HFONT defaultfonthandle = CreateFontIndirect(defaultfont); int state = SaveDC(dc); SelectObject(dc, defaultfonthandle); { fontwidth = defaultfont->lfWidth; fontheight = defaultfont->lfHeight; /* Only bitmap fonts (I think) have genuine sizes in the * LOGFONT structure. For other fonts, we need to query * the metrics. */ if ((fontwidth <= 0) || (fontheight <= 0)) { TEXTMETRIC tm; GetTextMetrics(dc, &tm); fontheight = tm.tmHeight; GetCharWidth32(dc, 'M', 'M', &fontwidth); } assert(fontwidth > 0); assert(fontheight > 0); } /* Count the number of available fonts. */ { LOGFONT logfont; logfont.lfCharSet = DEFAULT_CHARSET; logfont.lfFaceName[0] = 0; logfont.lfPitchAndFamily = 0; numfonts = 0; EnumFontFamiliesEx(dc, &logfont, (FONTENUMPROCA) font_counter_cb, 0, 0); } /* Allocate space for them, and then read them in. */ { fontdata = calloc(numfonts, sizeof(struct fontinfo)); LOGFONT logfont; logfont.lfCharSet = DEFAULT_CHARSET; logfont.lfFaceName[0] = 0; logfont.lfPitchAndFamily = 0; struct font_reader_cb_data data; data.index = 0; data.dc = dc; data.currentpanose = get_panose(defaultfonthandle, dc); EnumFontFamiliesEx(dc, &logfont, (FONTENUMPROCA) font_reader_cb, (LPARAM) &data, 0); } /* Check for the default font, mark it so that it always appears * at the top of the list (regardless of Panose data), and sort * the fonts. */ { for (int i = 0; i < numfonts; i++) { if (strcmp(defaultfont->lfFaceName, fontdata[i].logfont.lfFaceName) == 0) { fontdata[i].defaultfont = true; } } qsort(fontdata, numfonts, sizeof(*fontdata), font_sorter_cb); } glyphs = NULL; DeleteObject(defaultfonthandle); RestoreDC(dc, state); } void glyphcache_deinit(void) { glyphcache_flush(); if (fontdata) { for (int i = 0; i < numfonts; i++) { DeleteObject(fontdata[i].font); DeleteObject(fontdata[i].fonti); DeleteObject(fontdata[i].fontb); DeleteObject(fontdata[i].fontbi); free(fontdata[i].glyphset); } numfonts = 0; free(fontdata); fontdata = NULL; } } void glyphcache_getfontsize(int* w, int* h) { *w = fontwidth; *h = fontheight; } void glyphcache_flush(void) { while (glyphs) { struct glyph* glyph = glyphs; HASH_DEL(glyphs, glyph); delete_struct_glyph(glyph); } } static void unicode_to_utf16(uni_t unicode, WCHAR* string, int* slen) { if (unicode < 0x00010000) { *string = unicode; *slen = 1; return; } unicode -= 0x00010000; string[0] = ((unicode >> 10) & 0x3ff) | 0xd800; string[1] = ((unicode >> 0) & 0x3ff) | 0xdc00; *slen = 2; } static HFONT select_font_with_glyph(HDC dc, uni_t unicode, int attrs) { for (int fi = 0; fi < numfonts; fi++) { GLYPHSET* gs = fontdata[fi].glyphset; for (int i = 0; i < gs->cRanges; i++) { WCRANGE* range = &gs->ranges[i]; int delta = unicode - range->wcLow; if ((delta >= 0) && (delta < range->cGlyphs)) { bool bold = attrs & DPY_BOLD; bool italic = attrs & DPY_ITALIC; if (!bold && !italic) return fontdata[fi].font; else if (bold && !italic) return fontdata[fi].fontb; else if (bold && italic) return fontdata[fi].fontbi; else if (!bold && italic) return fontdata[fi].fonti; } } } return INVALID_HANDLE_VALUE; } static void draw_unicode(HDC dc, WCHAR* wstring, int slen, int w, int h, int xo, unsigned int attrs) { int fg; if (attrs & DPY_DIM) fg = 0x555555; else if (attrs & DPY_BRIGHT) fg = 0xffffff; else fg = 0x888888; SetBkColor(dc, 0x000000); SetTextColor(dc, fg); HPEN pen = CreatePen(PS_SOLID, 0, fg); SelectObject(dc, pen); if (wstring) { /* If there's text, draw it. */ switch (*wstring) { case 32: case 160: /* Non-breaking space */ break; case 0x2500: /* ─ */ case 0x2501: /* ━ */ MoveToEx(dc, 0, h/2, NULL); LineTo(dc, w, h/2); break; case 0x2502: /* │ */ case 0x2503: /* ┃ */ MoveToEx(dc, w/2, 0, NULL); LineTo(dc, w/2, h); break; case 0x250c: /* ┌ */ case 0x250d: /* ┍ */ case 0x250e: /* ┎ */ case 0x250f: /* ┏ */ MoveToEx(dc, w/2, h, NULL); LineTo(dc, w/2, h/2); LineTo(dc, w, h/2); break; case 0x2510: /* ┐ */ case 0x2511: /* ┑ */ case 0x2512: /* ┒ */ case 0x2513: /* ┓ */ MoveToEx(dc, w/2, h, NULL); LineTo(dc, w/2, h/2); LineTo(dc, -1, h/2); break; case 0x2514: /* └ */ case 0x2515: /* ┕ */ case 0x2516: /* ┖ */ case 0x2517: /* ┗ */ MoveToEx(dc, w/2, 0, NULL); LineTo(dc, w/2, h/2); LineTo(dc, w, h/2); break; case 0x2518: /* ┘ */ case 0x2519: /* ┙ */ case 0x251a: /* ┚ */ case 0x251b: /* ┛ */ MoveToEx(dc, w/2, 0, NULL); LineTo(dc, w/2, h/2); LineTo(dc, -1, h/2); break; case 0x2551: /* ║ */ MoveToEx(dc, w/2-1, 0, NULL); LineTo(dc, w/2-1, h); MoveToEx(dc, w/2+1, 0, NULL); LineTo(dc, w/2+1, h); break; case 0x2594: /* ▔ */ MoveToEx(dc, 0, 0, NULL); LineTo(dc, w, 0); break; default: TextOutW(dc, xo, 0, wstring, slen); break; } } else { /* No text, so draw a placeholder. */ MoveToEx(dc, xo, 0, NULL); LineTo(dc, xo+w-1, 0); LineTo(dc, xo+w-1, h-1); LineTo(dc, xo, h-1); LineTo(dc, xo, 0); LineTo(dc, xo+w-1, h); MoveToEx(dc, xo, h-1, NULL); LineTo(dc, xo+w-1, 0); } DeleteObject(pen); } static struct glyph* create_glyph(unsigned int id, HDC dc) { int state = SaveDC(dc); struct glyph* glyph = create_struct_glyph(); if (!glyph) goto error; glyph->id = id; int x, xo, w, h; /* Look for a font containing this glyph. */ uni_t unicode = id >> 8; unsigned int attrs = id & 0xff; /* Force bright if in reverse text; this makes reverse much easier * to read. */ if (attrs & DPY_REVERSE) attrs |= DPY_BRIGHT; attrs &= DPY_ITALIC | DPY_BOLD | DPY_DIM | DPY_BRIGHT; HFONT font = select_font_with_glyph(dc, unicode, attrs); /* Determine the size of the bitmap needed. */ WCHAR wstringarray[2]; int slen = 0; WCHAR* wstring; if (font != INVALID_HANDLE_VALUE) { /* There is a font for this glyph; calculate its size. */ SelectObject(dc, font); unicode_to_utf16(unicode, wstringarray, &slen); wstring = wstringarray; switch (unicode) { /* These are box drawing glyphs, and are handled specially. */ case 0x2500: /* ─ */ case 0x2501: /* ━ */ case 0x2502: /* │ */ case 0x2503: /* ┃ */ case 0x250c: /* ┌ */ case 0x250d: /* ┍ */ case 0x250e: /* ┎ */ case 0x250f: /* ┏ */ case 0x2510: /* ┐ */ case 0x2511: /* ┑ */ case 0x2512: /* ┒ */ case 0x2513: /* ┓ */ case 0x2514: /* └ */ case 0x2515: /* ┕ */ case 0x2516: /* ┖ */ case 0x2517: /* ┗ */ case 0x2518: /* ┘ */ case 0x2519: /* ┙ */ case 0x251a: /* ┚ */ case 0x251b: /* ┛ */ case 0x2551: /* ║ */ case 0x2594: /* ▔ */ w = fontwidth; h = fontheight; x = 0; xo = 0; break; default: /* This is an ordinary glyph. */ { SIZE size; GetTextExtentPoint32W(dc, wstring, slen, &size); w = size.cx; h = size.cy; /* Adjust size to cope with font glyphs that are bigger than a * character cell (italic or bold bitmap, or TrueType). */ ABC abc; if (GetCharABCWidths(dc, unicode, unicode, &abc)) { /* If this function succeeds, then this is a TrueType font, * so we use the ABC mechanism to calculate the overhang. */ if (abc.abcB > w) w = abc.abcB; if (abc.abcA < 0) { xo = -abc.abcA; w += xo; x = abc.abcA; } else { xo = 0; x = 0; } if (abc.abcC < 0) w += -abc.abcC; } else { /* GetCharABCWidths() failed, therefore this is a bitmap * font, and we need to use GetTextMetrics() to calculate the * overhang. */ TEXTMETRIC tm; GetTextMetrics(dc, &tm); w += tm.tmOverhang; x = -tm.tmOverhang/2; xo = 0; } } } } else { /* There isn't a font for this glyph. Use a placeholder. */ w = emu_wcwidth(unicode) * fontwidth; h = fontheight; x = 0; xo = 0; wstring = NULL; } /* Attempt to create the bitmap. */ glyph->dc = CreateCompatibleDC(dc); if (!glyph->dc) goto error; glyph->bitmap = CreateCompatibleBitmap(dc, w, h); if (!glyph->bitmap) goto error; SelectObject(glyph->dc, glyph->bitmap); if (font != INVALID_HANDLE_VALUE) SelectObject(glyph->dc, font); /* Initialise the glyph structure and draw it. */ glyph->width = emu_wcwidth(unicode) * fontwidth; glyph->realwidth = w; glyph->realheight = h; glyph->xoffset = x; glyph->yoffset = 0; draw_unicode(glyph->dc, wstring, slen, w, h, xo, attrs); exit: RestoreDC(dc, state); return glyph; error: delete_struct_glyph(glyph); glyph = NULL; goto exit; } struct glyph* glyphcache_getglyph(unsigned int id, HDC dc) { struct glyph* glyph; /* 0 is special, and means don't draw. */ if (id == 0) return NULL; /* Attempt to find the glyph in the cache. */ HASH_FIND_INT(glyphs, &id, glyph); if (!glyph) { glyph = create_glyph(id, dc); if (glyph) HASH_ADD_INT(glyphs, id, glyph); } return glyph; } wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/realmain.c0000644000000000000000000000416212245677273017301 0ustar /* © 2010 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. * * $Id: dpy.c 159 2009-12-13 13:11:03Z dtrg $ * $URL: https://wordgrinder.svn.sf.net/svnroot/wordgrinder/wordgrinder/src/c/arch/win32/console/dpy.c $ */ #include "globals.h" #include #include #include #include "gdi.h" #undef main extern int appMain(int argc, const char* argv[]); static int realargc; static const char** realargv; static uni_t queued[4]; static int numqueued = 0; static int timeout = -1; static uni_t currentkey; static LPVOID appfiber; static LPVOID uifiber; static uni_t dequeue(void) { uni_t c = queued[0]; queued[0] = queued[1]; queued[1] = queued[2]; queued[2] = queued[3]; numqueued--; return c; } void dpy_queuekey(uni_t c) { if (numqueued >= (sizeof(queued)/sizeof(*queued))) return; queued[numqueued] = c; numqueued++; } uni_t dpy_getchar(int t) { timeout = t; SwitchToFiber(uifiber); return currentkey; } void dpy_flushkeys(void) { if (GetCurrentFiber() == uifiber) { while (numqueued) { currentkey = dequeue(); SwitchToFiber(appfiber); } } } static VOID CALLBACK application_cb(LPVOID user) { exit(appMain(realargc, realargv)); } int main(int argc, const char* argv[]) { InitCommonControls(); if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "wb", stdout); freopen("CONOUT$", "wb", stderr); } uifiber = ConvertThreadToFiber(NULL); assert(uifiber); appfiber = CreateFiber(0, application_cb, NULL); assert(appfiber); realargc = argc; realargv = argv; /* Run the application fiber. This will deschedule when it wants an * event. */ SwitchToFiber(appfiber); /* And now the event loop. */ int oldtimeout = -1; for (;;) { MSG msg; dpy_flushkeys(); if (timeout != oldtimeout) { if (timeout == -1) KillTimer(window, TIMEOUT_TIMER_ID); else SetTimer(window, TIMEOUT_TIMER_ID, timeout*1000, NULL); oldtimeout = timeout; } GetMessageW(&msg, NULL, 0, 0); if (DispatchMessageW(&msg) == 0) TranslateMessage(&msg); } return 0; } wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/uthash.h0000644000000000000000000017125311523516526017007 0ustar /* Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H #define UTHASH_H #include /* memcmp,strlen */ #include /* ptrdiff_t */ #include /* uint32_t etc */ #define UTHASH_VERSION 1.8 /* C++ requires extra stringent casting */ #if defined __cplusplus #define TYPEOF(x) (typeof(x)) #else #define TYPEOF(x) #endif #define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #define uthash_free(ptr) free(ptr) /* free fcn */ #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ #define uthash_expand_fyi(tbl) /* can be defined to log expands */ /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhe */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho)) #define HASH_FIND(hh,head,keyptr,keylen,out) \ do { \ unsigned _hf_bkt,_hf_hashv; \ out=TYPEOF(out)NULL; \ if (head) { \ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ keyptr,keylen,out); \ } \ } \ } while (0) #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) #define HASH_BLOOM_MAKE(tbl) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ } while (0); #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv); \ } while (0); #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) #define HASH_BLOOM_ADD(tbl,hashv) \ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #define HASH_BLOOM_TEST(tbl,hashv) \ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #else #define HASH_BLOOM_MAKE(tbl) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #endif #define HASH_MAKE_TABLE(hh,head) \ do { \ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ sizeof(UT_hash_table)); \ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ (head)->hh.tbl->tail = &((head)->hh); \ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl->buckets, 0, \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_MAKE((head)->hh.tbl); \ (head)->hh.tbl->signature = HASH_SIGNATURE; \ } while(0) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_bkt; \ (add)->hh.next = NULL; \ (add)->hh.key = (char*)keyptr; \ (add)->hh.keylen = keylen_in; \ if (!(head)) { \ head = (add); \ (head)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh,head); \ } else { \ (head)->hh.tbl->tail->next = (add); \ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ (head)->hh.tbl->tail = &((add)->hh); \ } \ (head)->hh.tbl->num_items++; \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ (add)->hh.hashv, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ HASH_FSCK(hh,head); \ } while(0) #define HASH_TO_BKT( hashv, num_bkts, bkt ) \ do { \ bkt = ((hashv) & ((num_bkts) - 1)); \ } while(0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. * The use of _hd_hh_del below deserves special explanation. * These used to be expressed using (delptr) but that led to a bug * if someone used the same symbol for the head and deletee, like * HASH_DELETE(hh,users,users); * We want that to work, but by changing the head (users) below * we were forfeiting our ability to further refer to the deletee (users) * in the patch-up process. Solution: use scratch space in the table to * copy the deletee pointer, then the latter references are via that * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ do { \ unsigned _hd_bkt; \ struct UT_hash_handle *_hd_hh_del; \ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ uthash_free((head)->hh.tbl->buckets ); \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl); \ head = NULL; \ } else { \ _hd_hh_del = &((delptr)->hh); \ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ (head)->hh.tbl->tail = \ (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ (head)->hh.tbl->hho); \ } \ if ((delptr)->hh.prev) { \ ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ } else { \ head = TYPEOF(head)((delptr)->hh.next); \ } \ if (_hd_hh_del->next) { \ ((UT_hash_handle*)((char*)_hd_hh_del->next + \ (head)->hh.tbl->hho))->prev = \ _hd_hh_del->prev; \ } \ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ (head)->hh.tbl->num_items--; \ } \ HASH_FSCK(hh,head); \ } while (0) /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ HASH_FIND(hh,head,findstr,strlen(findstr),out) #define HASH_ADD_STR(head,strfield,add) \ HASH_ADD(hh,head,strfield,strlen(add->strfield),add) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. */ #ifdef HASH_DEBUG #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) #define HASH_FSCK(hh,head) \ do { \ unsigned _bkt_i; \ unsigned _count, _bkt_count; \ char *_prev; \ struct UT_hash_handle *_thh; \ if (head) { \ _count = 0; \ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ _bkt_count = 0; \ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ _prev = NULL; \ while (_thh) { \ if (_prev != (char*)(_thh->hh_prev)) { \ HASH_OOPS("invalid hh_prev %p, actual %p\n", \ _thh->hh_prev, _prev ); \ } \ _bkt_count++; \ _prev = (char*)(_thh); \ _thh = _thh->hh_next; \ } \ _count += _bkt_count; \ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ HASH_OOPS("invalid bucket count %d, actual %d\n", \ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ } \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid hh item count %d, actual %d\n", \ (head)->hh.tbl->num_items, _count ); \ } \ /* traverse hh in app order; check next/prev integrity, count */ \ _count = 0; \ _prev = NULL; \ _thh = &(head)->hh; \ while (_thh) { \ _count++; \ if (_prev !=(char*)(_thh->prev)) { \ HASH_OOPS("invalid prev %p, actual %p\n", \ _thh->prev, _prev ); \ } \ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ (head)->hh.tbl->hho) : NULL ); \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid app item count %d, actual %d\n", \ (head)->hh.tbl->num_items, _count ); \ } \ } \ } while (0) #else #define HASH_FSCK(hh,head) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ unsigned _klen = fieldlen; \ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ #ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN #endif /* The Bernstein hash function, used in Perl prior to v5.6 */ #define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hb_keylen=keylen; \ char *_hb_key=(char*)key; \ (hashv) = 0; \ while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ bkt = (hashv) & (num_bkts-1); \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _sx_i; \ char *_hs_key=(char*)key; \ hashv = 0; \ for(_sx_i=0; _sx_i < keylen; _sx_i++) \ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ bkt = hashv & (num_bkts-1); \ } while (0) #define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _fn_i; \ char *_hf_key=(char*)key; \ hashv = 2166136261UL; \ for(_fn_i=0; _fn_i < keylen; _fn_i++) \ hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ bkt = hashv & (num_bkts-1); \ } while(0); #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _ho_i; \ char *_ho_key=(char*)key; \ hashv = 0; \ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ hashv += _ho_key[_ho_i]; \ hashv += (hashv << 10); \ hashv ^= (hashv >> 6); \ } \ hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ bkt = hashv & (num_bkts-1); \ } while(0) #define HASH_JEN_MIX(a,b,c) \ do { \ a -= b; a -= c; a ^= ( c >> 13 ); \ b -= c; b -= a; b ^= ( a << 8 ); \ c -= a; c -= b; c ^= ( b >> 13 ); \ a -= b; a -= c; a ^= ( c >> 12 ); \ b -= c; b -= a; b ^= ( a << 16 ); \ c -= a; c -= b; c ^= ( b >> 5 ); \ a -= b; a -= c; a ^= ( c >> 3 ); \ b -= c; b -= a; b ^= ( a << 10 ); \ c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ char *_hj_key=(char*)key; \ hashv = 0xfeedbeef; \ _hj_i = _hj_j = 0x9e3779b9; \ _hj_k = keylen; \ while (_hj_k >= 12) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ + ( (unsigned)_hj_key[3] << 24 ) ); \ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + ( (unsigned)_hj_key[6] << 16 ) \ + ( (unsigned)_hj_key[7] << 24 ) ); \ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + ( (unsigned)_hj_key[10] << 16 ) \ + ( (unsigned)_hj_key[11] << 24 ) ); \ \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ \ _hj_key += 12; \ _hj_k -= 12; \ } \ hashv += keylen; \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ case 5: _hj_j += _hj_key[4]; \ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ case 1: _hj_i += _hj_key[0]; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ bkt = hashv & (num_bkts-1); \ } while(0) /* The Paul Hsieh hash function */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ do { \ char *_sfh_key=(char*)key; \ uint32_t _sfh_tmp, _sfh_len = keylen; \ \ int _sfh_rem = _sfh_len & 3; \ _sfh_len >>= 2; \ hashv = 0xcafebabe; \ \ /* Main loop */ \ for (;_sfh_len > 0; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2*sizeof (uint16_t); \ hashv += hashv >> 11; \ } \ \ /* Handle end cases */ \ switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 11; \ hashv += hashv >> 17; \ break; \ case 1: hashv += *_sfh_key; \ hashv ^= hashv << 10; \ hashv += hashv >> 1; \ } \ \ /* Force "avalanching" of final 127 bits */ \ hashv ^= hashv << 3; \ hashv += hashv >> 5; \ hashv ^= hashv << 4; \ hashv += hashv >> 17; \ hashv ^= hashv << 25; \ hashv += hashv >> 6; \ bkt = hashv & (num_bkts-1); \ } while(0); #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. * So MurmurHash comes in two versions, the faster unaligned one and the slower * aligned one. We only use the faster one on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ #if (defined(__i386__) || defined(__x86_64__)) #define HASH_MUR HASH_MUR_UNALIGNED #else #define HASH_MUR HASH_MUR_ALIGNED #endif /* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */ #define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \ do { \ const unsigned int _mur_m = 0x5bd1e995; \ const int _mur_r = 24; \ hashv = 0xcafebabe ^ keylen; \ char *_mur_key = (char *)key; \ uint32_t _mur_tmp, _mur_len = keylen; \ \ for (;_mur_len >= 4; _mur_len-=4) { \ _mur_tmp = *(uint32_t *)_mur_key; \ _mur_tmp *= _mur_m; \ _mur_tmp ^= _mur_tmp >> _mur_r; \ _mur_tmp *= _mur_m; \ hashv *= _mur_m; \ hashv ^= _mur_tmp; \ _mur_key += 4; \ } \ \ switch(_mur_len) \ { \ case 3: hashv ^= _mur_key[2] << 16; \ case 2: hashv ^= _mur_key[1] << 8; \ case 1: hashv ^= _mur_key[0]; \ hashv *= _mur_m; \ }; \ \ hashv ^= hashv >> 13; \ hashv *= _mur_m; \ hashv ^= hashv >> 15; \ \ bkt = hashv & (num_bkts-1); \ } while(0) /* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */ #define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \ do { \ const unsigned int _mur_m = 0x5bd1e995; \ const int _mur_r = 24; \ hashv = 0xcafebabe ^ keylen; \ char *_mur_key = (char *)key; \ uint32_t _mur_len = keylen; \ int _mur_align = (int)_mur_key & 3; \ \ if (_mur_align && (_mur_len >= 4)) { \ unsigned _mur_t = 0, _mur_d = 0; \ switch(_mur_align) { \ case 1: _mur_t |= _mur_key[2] << 16; \ case 2: _mur_t |= _mur_key[1] << 8; \ case 3: _mur_t |= _mur_key[0]; \ } \ _mur_t <<= (8 * _mur_align); \ _mur_key += 4-_mur_align; \ _mur_len -= 4-_mur_align; \ int _mur_sl = 8 * (4-_mur_align); \ int _mur_sr = 8 * _mur_align; \ \ for (;_mur_len >= 4; _mur_len-=4) { \ _mur_d = *(unsigned *)_mur_key; \ _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ unsigned _mur_k = _mur_t; \ _mur_k *= _mur_m; \ _mur_k ^= _mur_k >> _mur_r; \ _mur_k *= _mur_m; \ hashv *= _mur_m; \ hashv ^= _mur_k; \ _mur_t = _mur_d; \ _mur_key += 4; \ } \ _mur_d = 0; \ if(_mur_len >= _mur_align) { \ switch(_mur_align) { \ case 3: _mur_d |= _mur_key[2] << 16; \ case 2: _mur_d |= _mur_key[1] << 8; \ case 1: _mur_d |= _mur_key[0]; \ } \ unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ _mur_k *= _mur_m; \ _mur_k ^= _mur_k >> _mur_r; \ _mur_k *= _mur_m; \ hashv *= _mur_m; \ hashv ^= _mur_k; \ _mur_k += _mur_align; \ _mur_len -= _mur_align; \ \ switch(_mur_len) \ { \ case 3: hashv ^= _mur_key[2] << 16; \ case 2: hashv ^= _mur_key[1] << 8; \ case 1: hashv ^= _mur_key[0]; \ hashv *= _mur_m; \ } \ } else { \ switch(_mur_len) \ { \ case 3: _mur_d ^= _mur_key[2] << 16; \ case 2: _mur_d ^= _mur_key[1] << 8; \ case 1: _mur_d ^= _mur_key[0]; \ case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ hashv *= _mur_m; \ } \ } \ \ hashv ^= hashv >> 13; \ hashv *= _mur_m; \ hashv ^= hashv >> 15; \ } else { \ for (;_mur_len >= 4; _mur_len-=4) { \ unsigned _mur_k = *(unsigned*)_mur_key; \ _mur_k *= _mur_m; \ _mur_k ^= _mur_k >> _mur_r; \ _mur_k *= _mur_m; \ hashv *= _mur_m; \ hashv ^= _mur_k; \ _mur_key += 4; \ } \ switch(_mur_len) \ { \ case 3: hashv ^= _mur_key[2] << 16; \ case 2: hashv ^= _mur_key[1] << 8; \ case 1: hashv ^= _mur_key[0]; \ hashv *= _mur_m; \ } \ \ hashv ^= hashv >> 13; \ hashv *= _mur_m; \ hashv ^= hashv >> 15; \ } \ bkt = hashv & (num_bkts-1); \ } while(0) #endif /* HASH_USING_NO_STRICT_ALIASING */ /* key comparison function; return 0 if keys equal */ #define HASH_KEYCMP(a,b,len) memcmp(a,b,len) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \ while (out) { \ if (out->hh.keylen == keylen_in) { \ if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ } \ out= TYPEOF(out)((out->hh.hh_next) ? \ ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \ } /* add an item to a bucket */ #define HASH_ADD_TO_BKT(head,addhh) \ do { \ head.count++; \ (addhh)->hh_next = head.hh_head; \ (addhh)->hh_prev = NULL; \ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ (head).hh_head=addhh; \ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ && (addhh)->tbl->noexpand != 1) { \ HASH_EXPAND_BUCKETS((addhh)->tbl); \ } \ } while(0) /* remove an item from a given bucket */ #define HASH_DEL_IN_BKT(hh,head,hh_del) \ (head).count--; \ if ((head).hh_head == hh_del) { \ (head).hh_head = hh_del->hh_next; \ } \ if (hh_del->hh_prev) { \ hh_del->hh_prev->hh_next = hh_del->hh_next; \ } \ if (hh_del->hh_next) { \ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ } /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of * the hash function as it applies to the key domain). * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write * * ceil(n/b) = (n/b) + ((n%b)?1:0) * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ #define HASH_EXPAND_BUCKETS(tbl) \ do { \ unsigned _he_bkt; \ unsigned _he_bkt_i; \ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ memset(_he_new_buckets, 0, \ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ tbl->ideal_chain_maxlen = \ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ tbl->nonideal_items = 0; \ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ { \ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ while (_he_thh) { \ _he_hh_nxt = _he_thh->hh_next; \ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ tbl->nonideal_items++; \ _he_newbkt->expand_mult = _he_newbkt->count / \ tbl->ideal_chain_maxlen; \ } \ _he_thh->hh_prev = NULL; \ _he_thh->hh_next = _he_newbkt->hh_head; \ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ _he_thh; \ _he_newbkt->hh_head = _he_thh; \ _he_thh = _he_hh_nxt; \ } \ } \ tbl->num_buckets *= 2; \ tbl->log2_num_buckets++; \ uthash_free( tbl->buckets ); \ tbl->buckets = _he_new_buckets; \ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ (tbl->ineff_expands+1) : 0; \ if (tbl->ineff_expands > 1) { \ tbl->noexpand=1; \ uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ } while(0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ /* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ do { \ unsigned _hs_i; \ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head) { \ _hs_insize = 1; \ _hs_looping = 1; \ _hs_list = &((head)->hh); \ while (_hs_looping) { \ _hs_p = _hs_list; \ _hs_list = NULL; \ _hs_tail = NULL; \ _hs_nmerges = 0; \ while (_hs_p) { \ _hs_nmerges++; \ _hs_q = _hs_p; \ _hs_psize = 0; \ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ _hs_psize++; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ if (! (_hs_q) ) break; \ } \ _hs_qsize = _hs_insize; \ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ if (_hs_psize == 0) { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ _hs_e = _hs_p; \ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_psize--; \ } else if (( \ cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ ) <= 0) { \ _hs_e = _hs_p; \ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } \ if ( _hs_tail ) { \ _hs_tail->next = ((_hs_e) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ } else { \ _hs_list = _hs_e; \ } \ _hs_e->prev = ((_hs_tail) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ _hs_tail->next = NULL; \ if ( _hs_nmerges <= 1 ) { \ _hs_looping=0; \ (head)->hh.tbl->tail = _hs_tail; \ (head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \ } \ _hs_insize *= 2; \ } \ HASH_FSCK(hh,head); \ } \ } while (0) /* This function selects items from one hash into another hash. * The end result is that the selected items have dual presence * in both hashes. There is no copy of the items made; rather * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ void *_last_elt=NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ if (src) { \ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ _src_hh; \ _src_hh = _src_hh->hh_next) { \ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ if (cond(_elt)) { \ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ _dst_hh->key = _src_hh->key; \ _dst_hh->keylen = _src_hh->keylen; \ _dst_hh->hashv = _src_hh->hashv; \ _dst_hh->prev = _last_elt; \ _dst_hh->next = NULL; \ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ if (!dst) { \ dst = TYPEOF(dst)_elt; \ HASH_MAKE_TABLE(hh_dst,dst); \ } else { \ _dst_hh->tbl = (dst)->hh_dst.tbl; \ } \ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ (dst)->hh_dst.tbl->num_items++; \ _last_elt = _elt; \ _last_elt_hh = _dst_hh; \ } \ } \ } \ } \ HASH_FSCK(hh_dst,dst); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ if (head) { \ uthash_free((head)->hh.tbl->buckets ); \ uthash_free((head)->hh.tbl); \ (head)=NULL; \ } \ } while(0) /* obtain a count of items in the hash */ #define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0) typedef struct UT_hash_bucket { struct UT_hash_handle *hh_head; unsigned count; /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. * (The multiplier is simply expand_mult+1). The whole idea of this * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; } UT_hash_bucket; /* random signature used only to find hash tables in external analysis */ #define HASH_SIGNATURE 0xa0111fe1 #define HASH_BLOOM_SIGNATURE 0xb12220f2 typedef struct UT_hash_table { UT_hash_bucket *buckets; unsigned num_buckets, log2_num_buckets; unsigned num_items; struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ /* in an ideal situation (all buckets used equally), no bucket would have * more than ceil(#items/#buckets) items. that's the ideal chain length. */ unsigned ideal_chain_maxlen; /* nonideal_items is the number of items in the hash whose chain position * exceeds the ideal chain maxlen. these items pay the penalty for an uneven * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash * function isn't a good fit for the key domain. When expansion is inhibited * the hash will still work, albeit no longer in constant time. */ unsigned ineff_expands, noexpand; uint32_t signature; /* used only to find hash tables in external analysis */ #ifdef HASH_BLOOM uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ uint8_t *bloom_bv; char bloom_nbits; #endif } UT_hash_table; typedef struct UT_hash_handle { struct UT_hash_table *tbl; void *prev; /* prev element in app order */ void *next; /* next element in app order */ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ struct UT_hash_handle *hh_next; /* next hh in bucket order */ void *key; /* ptr to enclosing struct's key */ unsigned keylen; /* enclosing struct's key len */ unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; #endif /* UTHASH_H */ wordgrinder-0.5.1.orig/src/c/arch/win32/gdi/utlist.h0000644000000000000000000005535211523516526017040 0ustar /* Copyright (c) 2007-2009, Troy D. Hanson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTLIST_H #define UTLIST_H #define UTLIST_VERSION 1.8 /* * This file contains macros to manipulate singly and doubly-linked lists. * * 1. LL_ macros: singly-linked lists. * 2. DL_ macros: doubly-linked lists. * 3. CDL_ macros: circular doubly-linked lists. * * To use singly-linked lists, your structure must have a "next" pointer. * To use doubly-linked lists, your structure must "prev" and "next" pointers. * Either way, the pointer to the head of the list must be initialized to NULL. * * ----------------.EXAMPLE ------------------------- * struct item { * int id; * struct item *prev, *next; * } * * struct item *list = NULL: * * int main() { * struct item *item; * ... allocate and populate item ... * DL_APPEND(list, item); * } * -------------------------------------------------- * * For doubly-linked lists, the append and delete macros are O(1) * For singly-linked lists, append and delete are O(n) but prepend is O(1) * The sort macro is O(n log(n)) for all types of single/double/circular lists. */ /****************************************************************************** * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * * Unwieldy variable names used here to avoid shadowing passed-in variables. * *****************************************************************************/ #define LL_SORT(list, cmp) \ do { \ __typeof__(list) _ls_p, _ls_q, _ls_e, _ls_tail, _ls_oldhead; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _ls_p = list; \ _ls_oldhead = list; \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _ls_q = _ls_q->next; \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ } else { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ } \ if (_ls_tail) { \ _ls_tail->next = _ls_e; \ } else { \ list = _ls_e; \ } \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ _ls_tail->next = NULL; \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define DL_SORT(list, cmp) \ do { \ __typeof__(list) _ls_p, _ls_q, _ls_e, _ls_tail, _ls_oldhead; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _ls_p = list; \ _ls_oldhead = list; \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _ls_q = _ls_q->next; \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ } else { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ } \ if (_ls_tail) { \ _ls_tail->next = _ls_e; \ } else { \ list = _ls_e; \ } \ _ls_e->prev = _ls_tail; \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ list->prev = _ls_tail; \ _ls_tail->next = NULL; \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define CDL_SORT(list, cmp) \ do { \ __typeof__(list) _ls_p, _ls_q, _ls_e, _ls_tail, _ls_oldhead; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _ls_p = list; \ _ls_oldhead = list; \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _ls_q = ((_ls_q->next == _ls_oldhead) ? NULL : _ls_q->next); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _ls_p = _ls_p->next; _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else { \ _ls_e = _ls_q; _ls_q = _ls_q->next; _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } \ if (_ls_tail) { \ _ls_tail->next = _ls_e; \ } else { \ list = _ls_e; \ } \ _ls_e->prev = _ls_tail; \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ list->prev = _ls_tail; \ _ls_tail->next = list; \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) /****************************************************************************** * singly linked list macros (non-circular) * *****************************************************************************/ #define LL_PREPEND(head,add) \ do { \ (add)->next = head; \ head = add; \ } while (0) #define LL_APPEND(head,add) \ do { \ __typeof__(head) _tmp; \ (add)->next=NULL; \ if (head) { \ _tmp = head; \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(add); \ } else { \ (head)=(add); \ } \ } while (0) #define LL_DELETE(head,del) \ do { \ __typeof__(head) _tmp; \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ _tmp = head; \ while (_tmp->next && (_tmp->next != (del))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = ((del)->next); \ } \ } \ } while (0) #define LL_FOREACH(head,el) \ for(el=head;el;el=el->next) /****************************************************************************** * doubly linked list macros (non-circular) * *****************************************************************************/ #define DL_PREPEND(head,add) \ do { \ (add)->next = head; \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev = (add); \ } else { \ (add)->prev = (add); \ } \ (head) = (add); \ } while (0) #define DL_APPEND(head,add) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev->next = (add); \ (head)->prev = (add); \ (add)->next = NULL; \ } else { \ (head)=(add); \ (head)->prev = (head); \ (head)->next = NULL; \ } \ } while (0); #define DL_DELETE(head,del) \ do { \ if ((del)->prev == (del)) { \ (head)=NULL; \ } else if ((del)==(head)) { \ (del)->next->prev = (del)->prev; \ (head) = (del)->next; \ } else { \ (del)->prev->next = (del)->next; \ if ((del)->next) { \ (del)->next->prev = (del)->prev; \ } else { \ (head)->prev = (del)->prev; \ } \ } \ } while (0); #define DL_FOREACH(head,el) \ for(el=head;el;el=el->next) /****************************************************************************** * circular doubly linked list macros * *****************************************************************************/ #define CDL_PREPEND(head,add) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (add)->prev->next = (add); \ } else { \ (add)->prev = (add); \ (add)->next = (add); \ } \ (head)=(add); \ } while (0) #define CDL_DELETE(head,del) \ do { \ if ( ((head)==(del)) && ((head)->next == (head))) { \ (head) = 0L; \ } else { \ (del)->next->prev = (del)->prev; \ (del)->prev->next = (del)->next; \ if ((del) == (head)) (head)=(del)->next; \ } \ } while (0); #define CDL_FOREACH(head,el) \ for(el=head;el;el= (el->next==head ? 0L : el->next)) #endif /* UTLIST_H */ wordgrinder-0.5.1.orig/src/c/arch/win32/icon.ico0000644000000000000000000036323512243251035016215 0ustar  (h h&  v  00'00 %n6 \( "#+36DNL^kVo~`|plz4wݢfujji/%ROj@J|89 Apnffffooo AA_(    "'#(#'&+ )/$+/",1'.3%/6*5=-9?1?H5DN;FL:JUCU_BVaJ\hH]jQ_gK_mL`nMdtOeqYgpSjwTl|Um|ZqXqXqYr]w]wdy_y`z`z`{a{a{b{`{a|b|b|b}a}a}b}c}c~c~ddpfshjklmnnn}utu}xy}}Bw #DWR]}5.dkG)-yj`cb6Z{?qf$+a\OE=S4<:28K 17*,69 AA_(  *Pkw@':L]of^kv Yhppܖ#'L #;FLYgps㊟!n$+/̦.`{y_ykXqdy㍮8d4a| c~"Sjw]w3;Ab}u`zy*5="''.39`zVJ`mv1?Hb , d",1Tl|"*.rW Tl{(Um|&+f8OeqBB:J\hQ_g#Xqfa{H]j  ~3AJvBVa鄩4^yfL`n:JUmZq5DNxUb}nMdtK_m d%/6EQWya|j-9? )/}j l}CU_d}Xr@a{XqleMb}{a}nuc~@G[lthc}}bt~h_{Eb{b}a}a{b|n#(i`{b|`{Yrq4ENy]w?_z|a|b}< ( @(/+7@;IRH]k[o|a|nn~4hw̻2x̻we ۻ0}lw0  ow0܀w{0OPo0?Pwb\ww̐0 x0 r,`gy<'6@189)8A*9C+:D0:C,;E0?J3BL:FI;GJ6FP8GR?IR@JS;JU;KV hF hGƽHc`ҢxTKƤMTaSʱ9oVkcT\aM_v{oMTVYTS\T^YZ\aSTSRk{VS\\``^MMS\T`aWVR_YV\VTMVYTYTTT]UE꺪 @(  _ T* @  @@pp( @  .;IV^V= $+3<EOXblvwgB-MgymA&&6FR]gq| ^) Fs=GLhyۑ jHHb~!^wUa{`{w.;B}bv{ ~mA! [tdeo+8@" L`}qgj|w!b~^y]y.;D -:B:JUsj~H\j%,~m~M@! cb}\vriNfv#vf89IT(|d*6> hyLdsUqy`3Mvp[G3" k. .;B>OZMGddvN\eGNft9Rkz slU>*uq{Z{T?\㡽\& _y{b}dTkz&18ybL:+$1OnI^kp5^y`{`{!&u\JO"({@JRF\vc~c~t2@I(29 woovY".AFa|`zh-:A{^y#-3tEYg(.Perl1Tk|Vb~a}qw*5iWmz*6<'-p+鄩i^xbs}Zsnefz}p]"}}^y`z\v{|_yG~b$(pa}b~a|y~H\gi1o'yka|ovAe`{b~_z^yc~ppx}~~ P q+vl]v_{b|c~a{`zc~d~-_{b}b}b}b~b`za|`z^xkq|Qgwd7f`|_zc~b~b~dc\vUo~f `z\vVbzb~`|ddb|b|`yca}X: a|c~c|`{_zMdrU [v0\vn`|a}`{a}`{ dlb{Un}9 )HNo["%nJV]`q}vߎ󕯿#),qG%^xQ"pՎ杶DPYxM)Sjz]xkv ct~T.Wq~c`zez}c#(-ǣ[3?R__yd]w[uo}}>P\_oyۤ~{{_7]wr`zb}`{e~crꋫ~}~ `8a{Ee`{d_{|Zt 4CL)5;!+2}3=BY5Ytjf`|fw~K`m]vxVoӅ}}qe_nwlJ,db}`{a}o}~Lcq xPgsNesp~}zdP6]x`{a|b|hUlw+7?{t2ALniˁ=LWF[i@4"a|9ddigPguNeta{O3CK`+7=$.45! ce[vc~uMcs!^y$)6-qj[lt @* xa}a|`z`{pVm}Um}$ mOdr*vR|p$/7GR[{ ]VJ4a}la~az`|j}|xg:LZ]3@J8BTaA4~ȁUm~ %em:MXwUgrpaE)^zcMYrM>O]A4CLU2AKk f`ZPB6,$ x1?I _y $/5g&08WFZ y4^z}paTE9+ oD  mKDFWi8AH뇟hA"KcrFH]kkSr|m^N?2& a*>Ob |HU^!$xO+]xa}XrDWc"&vgWH:/  4G[n!+2ޠFRX\5 hdcehh5DN paSF:0*.;Qg^z~i{i@! FWd"\wb}`zc~x`{ |o_ODERp5FPz vL)PgwK`{`{d_yNeso\X+8@Ys#(,Y3Wpr^yb|_yj|%*ld} }^w̓{\lvg>  Zr`|b{b}hf&18~w Ncpk;MV lg=MXsJ'2BK`|a{_z_{w}!Qgvz!*0&2:Qix p0=F_z#)-W0;LX!`{b}c~a{}[u }z7DN m  uj wgxd;FXg3az`{]xd|wCWe}H\h}6EL |}DUbH ?PY7GOa~+6=b:gbvx|9BI_xed}kk~~Umz)5<~V0WrDx|{~}u`{d]xa|s~}u:JUkipH'܅yMds%a|a}_y]xyz5DMa:P]gybYrLdea}^z~!%wP-~I}}rhjQDdsa}_{b~b~fkru{~wf@!pK}vpib}^z_{a{]wf{8CHJ'?Te`{d`{e`|c`{[u]xb~efqs}~DXgzU2 }Uxl`}a~^x`}_{d}`{c~`z`zb}_ygQ`h4az c~\wa|c~_yeca|c`}_y`}^yd_ykm~~udD&n:ca|^yca{b~cddca|a{_|Sj{b]z_{@b~sdb{b}c{b}b~b~b~c~b~d~`z`|b|`|_zbkEZhXJ1 _{d_zcb}`|d_{]x]vOgu]4BK"NblTkzTm}^u/\wuc~a}a}a}db~b{^wc}d~cfb})/V9/_za{ga|d}]x[uz=OZ2,9A Ws&_zl^y_z`{a|a}a|cb}a}b}2b}b{^y?P^#`(`|QYrz`{b}`{^y c~di/8KWZqWqUmxPgv~8  ??8 008xxx?PNG  IHDR\rf IDATxye],jMkna0aG "0 0cgp3e.$ "VU{9'TI_WUI-8qNYs}/:<^A>}8GRGu_I Sn;N:0@Ǣn|؄Bp=Wm^W~'N^N:p}0-M‡uA@s4M|wO]/M)iO~Gd2.mCo{Iisux,Lŷ~X [Qwp|{]>vh|$5'G'k=hQ6<ܼ|H`02 56}EG/0ڰ6()-W0q=Q=,#7Vx߷o RQrBJ*)xN ^|w?g0~~ ?7#Ϻ(Gߨvp5k]$K$"$"%xҖo{G޺or8?qHpD >V g{9CkCB8aItvܾ>o7_qC|C8q zG+P-!O!USLۑq ɣA+2"{k΢ehFhl.Ѹ%MCF(K_+J(9BXCt˞fY>PJA;1&JRQ*&u4TC RiۖYLۢ8k E ՠq L@ [u;ƺیm80wXרkix>#dqvuB4lK}XLSbo'k{d zNZ's`=}/oA6W/ $ҏj?LJNjD:yGk Ϲk|kn<dCBۃc5EO*Vm3S( l[.J+bh*>DDkEH)f"Tq*$>x=c[CJ)`6,KR PYkTCIADU1,@*Ĝq_{go岻ӡ5r̳8nG?/yru9|Q#Ehzk*<]0-A%B4 UBBXLG(Uvqc?Ko??G夲{yǚ8rϴʼďK8@YuPR"Pƙ*DhݓRZ6@@IM JiDe2(!A )Et*`sAK2FJa;1m۞FKj$9K 9uR)TQ)1c%L2Mⱶg$s@J2%@HiؐB:m{T=E*RX߼7\\K-J? -G}1cA;s?M;6ϓR iMH0[6}yamB)EDI*Pڢ"`Cբi[,(DbQ(Q0R"`=Hi|oޟo|}'8ʈ#םT'gH-ϸik>IzR]ww]ص&m& k; ZSU%QHb tj*S&HRdh@\RKX2MӠD4ّ;,24xr\lK\c0ƐQ )l(ARE(akz D #R)i %3+Rjk,JL ZVœۀwÂ.r\8܈塴+xq26MG,Xg $hB1ZiZ6Ԫ6a5ŊS7J6BXԔCo=;wVwo/{{~]?S;rl9t|-ãqB~7On^w<ݱĻ6N*œrAE nPZXGNL 4.zӴM:jX e5G4*saT"Y-4Ӵ%Q@ܮlcQ0#%,O"dĶh~ĞZsĒRbI):ZHR2 BvK64A ( ՄZbfca,[s j8ehGञ Ccv"gRB Cu(Hݶ5-lj=>e|.lj<)x@!cK$1DH 5GĻ{/>6nEfE!wuOWtW Ͼ?^o/H\ 8IKt-}T`p=xWzZ7//]Z-1(8,4H0vVӘƁ0``gJ]ΊMkR0L#k0B`#iZI"?,g#G"6̶\`݂=6H%Q8pR$a ԚR Fڶ3 ]ײ!@qjgvh9y0*"jPdRFO)iPV2Hdsz:z;z-W4J##+aQk|/1eȉByvK !!D:C(cY,h-;ԐR,T 14 µ4gu)_1O7/=pio{+$qpC x-o_ȏ7Ҿ-=v\8DrXaE0lm:rrek Bʕq,KT '´%gZ7JDa[*\iPB9Eu`Cĥ햮m+{TM Ri5H)E(买!k!ŴIX/ !PD@T.v9b-{J-.!YQ1EDhCk-D n4TC !b-1FJ H-ڰ?3gu ڟ7?1n7g`o33adElӐEfZd,HLg!.mV3YPȔ$pg_ (c]RTۡd\o=RUj)0*}ov"!/r.9  s^*oeK~2|70д-JΙ0jU+FvԔ#Vq]Oc,I\fvڑH3# J1dFw-JHلZj"B(ݝ}gv;(%yj4A[I'e\r/َs!й"!aZRhkU"m%LF^YL@dǩ ]cRs(9{ 0"r%fxw?T+[<#&s\>q?!*ϜZlцHB43*d dj*@+V}Z#$!1)HS5H4Sh0m^9}$St]q-JsV$Z2>b okӯCH&`VY^kpp]* z_yk v޹J{f j&HHD= R &,Vf ,#I.>M8%MŜ24^qH 9f+5hhԌ>R q6>FM Yzi3 D,]/PFLʅgβnQBbF+f(0av b 9(#)@Y`%&daYGi`#)GV43M Ɗhɩ">x.+{tE࣍8ë  1Ϻzo:sBgWR)Jl~@"pHAP+Zk[hgP :㪓]bJ lcYѹ'"xeGK JYMqXi%i؎\#Qo"8`2})`oFJH]VӶ=RARj4 XcVƉ4נ&FPF PB/{R+Ren'/RpF`gù+NZ!ňbd(`fIPʷ~UTCsGop:{\{?}^۶[J-VZC*ɰQ vCBӄ0OpQ+β.4DT#QԚՠB肨 )'VHg'XU4e@T Ȁf;lqa7QkPHjL) J)wk_>،9 *\kH4uTKk01H0M8lgPQv5*C ggJ!iZS2)"5Ehۖr#%EP )-"l:mv1Ke3l88Ԝ`A[êiZ (1{ﵽ%L525'J̰3kF-NjH"9BH!PBN1Zl:ΞZ')7泧{'e[6ۑK.r` di`䶛Zz줱ac>3^̶q_h(BerJ9{= c-(9ǥK{\*Ew4]2BsRbBɂR2 Ab{ѶtmM$ú(4(hTA s&DՊ*FVI ~$0#J q91;g ?k?E}=ibڵ~rR~I*/?{R*ά{|{k! q$iR(rAHs?QWI1g8k b$Ȍ! h4P#"'|(ӰZ4(א'50ٌ! 4knX.f3p6&rh,B1j}f^7~GK">D \5z ςyiTڮc\`CL Dk6j"ROd"i\+ w,*nynq\ǁ2riU(B@ k4JkmX-"t0e IDATٿfF]ƐRD 4E(jɔAV}")z0*))aq۞tOUiGDBILɓbaXo0Nᜦoܙoyە'GqSI?賏: _h>~gm|@6Ͱ\ Xo6[RĐR)9R L ^¼ )GJ3/ +PBs$Hm$抨ƒk|к2R"uc J 䘩dq QSHN-8ѷcng~3t{z޻K\7cX!BhżlZɔ`#WIM R8 YSҊFʙ`\{9%+E*Dg} eI`<<cƏ#Vj L$?j1f/\m[uX5JΉZoR0LZѷ_ H-<۟y_5?ze2?^wXk~?Wr@%m| S$$Jr b%)0Z l7l96 ZI%Q@sh WޣEf2[p!FiƢQHef|}+3֭v$6kZ]k5=tk9، ci{5S4S&E [bEaFR)܀ZmZyb2t ` ~GZf_~[V (9xxqE^it??1ndO*gKl,t|*, D\2B٧CVd-tn}%㌥[Pd#P%做+SX(5!`? Ф2StH kE ZSDEB*m(,&. eagm4>%J.lRJؖXyLۃ{{xof;D"4lxߵ'w}9ږJ6:mB3d$J6.ӻXjэ~ Pgqnv,f\) 4=ajB*%%nѣ" s/.,y N|<20yQ4\ƀZdQ4&whA C 0!J [RM8gڝyګ y0n:sw}swoFpN~ >]n4P:?:i=ﶛn~g|Ƨ~'?Jp(o450drq!$K0iO"m3b^AvDQ:UE`HJwJ- MעŎ[넏AJC dΑF*vN8z]h]Lǁa򂝅iwל:`ʳKq3݌V;X)Y9Yv r41@.!HLcDH-+]rbo6-zAΊi>e:Ҙ`!HvO-Yh6[ IٳMz'џx\隇 +iW8wa08\>%TqXtP@ȌYqn1YrfFtjAP{ܿޠr|TrJ@TI &(Iދl'g-ӴǢ[/VƱ?l55-8F9k-9 lO#qC3C*VJkqdv% #!"%5+,,*Ubݒ>ixZ~8}9sr,o>R3B2fb׳3Lӳ)H41T#Y"$HFvӴGb*(%Rl9:GZ.7<!CMihTJLƶnim%7u= )1u!BXpԒ{{/FCkb {?ҚY/ ?/%Y[-BcfgbXYdqp<Ì22#Qd!Tu8H?X)C\,ݮ]jQDB)TBߑzՑ{t}oxp}ãM[Q!HU5hk{oOXs<}tc O'H81Dv#*bI$23T T0mh 9&8AYLP dE 6Պ@)Wn]]a:qq8xrƔhZ~pe'm A R )Hx!a!$_EMn. y @I2<ܽA,G{&Fl隚k ι(A:PF\\? Urz>S+iRWl37XI0G)|ƚ2]E%5 ?z +N? 0O#!C ©Husf8M*Ֆ@B["9|IRJX!35+FOP +D~}+C \2gn?Eip&3bdzxG|tr ~aT;ӹ~JA,"$)yYbEw~ p8yG5Ƙ)wT%5!%ݼEX19ANֈ,I)AQ0m-F\ӸjER!b㦡mJ,,K"4C^p< 8ẖERYS.%)o.Ԇr{A綨h^{&d)jfW TwyE?GHșjHT/ k4L R5vsA,t!y檽ZI\`9v NyaM]qUfbCm%=̇#f5չ77 }fz >&ǟ)+me1G9"gk/ifXfh T$ 8٠ V@Fq&.Z9rOt;rqɤR}dzb\n;ĘYZĕ .DL+t|W]zJP&!hAU+i-녴ض+ĔXBB G7?Z<.UXԵFG yYEF@SO6ή$$ܳDж5FTLaBeYZ`MUb s*,!" B6.&.daU\^]rkg #Fkwb ,W5b%ti]v 'G[sƹП,J9nqoGf̦zöBĴ$ +Q$BNhyi m,W- {aFN)^$C?㇉yZhI.js5bR'Lv-RD7Hh$vbS~g{J̘jecim9 q 7߸i}eiϿ> ~gCi-6+4H~BVmk.<(ILk=Lc8rѶc B(b Ba]E3%fIhHHL _~o}3^WC~ZSӿ<}XSB YV4gjq: ~A *c(ubaYǞs?=& )lmpLa,_]q`S?/"s?ލZ$Z)ְ́9x+.wH%6 ʀ1_>H91 Zkv|sΦmIΠ BNWFe0.yf ?_~.q׹o<_wE(R\GVV;@c VAK)eeF鶌a^Fvs zr \_n)if8YKm*mN\~ M6TĝܼRm`Rb0ʭٴ-HNsVGy@$ P 9J*(j)E %Q\㡇Jeuy5|H)r:y]RY*U+<+8 DhU1OcoJ T~ ~F)ƱLi]k7Zf $ `\j 9E)q,"$0e ٓ|ddәgd)4MK L$е™ 5Ԃ"_y)|']^{_&!K"#2Vw:^L翙^_+_'` x:[or0u֊'Ϟ2 3વ~M TUZJ ,K< Eޓ/j~ xޫߌ,O(i ߉)8 0ER)0 U)DȖ%z8oް,9SWBbN]"JfqKV}! FDn8o˓Q閺nUq8g8<]ir.r0M `ZEOHV$ZvdC+_`>яkvm@C#.vhy4\li.)~!"(JYru6%d )'}wOLuuj[?=@4Mf/cK^nJNyx˸nci[6+OԠ0Pr"H2R0xG4mi䶡jkCh{ >r{أ] XWa%%dR0eee2VT*>) i`=ED$.coOD!)^lYШ)ؖhlMc9`˹',jxf<"Z[s8-(E/}o;G>L}U o??vq8"X4MGNBru]U\_ojPж\:*#6v R(ea8Xbcj@(dQH UעQcO,lie|׹z@"p~RRy]~Xۧ̃SL3=z{KK,Ña< MSCϗ#\6'Z' ,Хܼ2Ó $laHW74UkPjGH3mC/%!LX],iSJS+W~o^x9]1O6wy`'PZ✣2$! C(I"2hJK(5)\^R~Ym|lq<ۧL'(m.qHC ie%kPy-Lvm⧅pk6l[Ts !p{D[_pHl.y!2~Dh@C? <}5I 3u͓ߦNY)Ee8" qI䱦"D#F G;2$hZ d9?o*|155FB?A+~eXNgJaԫf @\VHD >DbHJJ"8km:Yt%Lmķ|!3Y4 p%r[TfR"ӁP4³,/tO1fZ!;SɯPҴ^ǂY0?-g KX11 hd{NS54|@ĸ̨1fBb@IKQ3a$Q)${1R50FH)`W͗5>np?}~w؍j>D|3ęe6V43̑yVh FIXQc&⼠ m7Wi,|fV߈4M]癓,H1SW }}yY-g6k-0q:@ewZWHeHZ<ƹaV~RB ABRUGJ҆"">ebxRYl gb,Tºju3Y61A@8G6V!&qƭvNഡL$caY03EĚdABiI]m+1NS |{RB1GZgRc\-XtqǑk rXꚛGmpuQԧ_޾ ?8%O.?5babBUmYE@5U~fP+૒+nJU%!"UjQsnkjH T烐\t )Gkhۢv 鈏z1F j "dA׎2}?EmwۯOfa % RdZyC ^RW~g}^M?Ԙ _:} J|"rՕV()!)VD!4*%V2BHZc!悟F{}qqkm]WfCHx&HZΔ1Eu[撋ŃP%RZ(#X]vKX2ıvBin/PJ31Z2O{vqRD rOOR2$s8A(n 0 ǧ؟f2-SYRfFd^}2(JcTaZ#8Bs-[L%z?U?lZ>|N܀//l6@s9 TVTH(1ؒ SRv\Ti<,f{MU9*`*t{%Fg "HٱinPx&i41Ag  ڮ ]ې=P=|D Hf?=;bA* [RU@h9PBef:l6v˒駞a2q_7fy7-{#)-->U]H%F]n RD,OBaB'u;LjU8!8yx?c&DҚf1!!E*+ZSkV2P)ٵȄ0r{llÇ'xWu8oIiG)D%y#\"|`{I6!@RFBDSpq5c>NDI%PM3B(vM?zp2,䥍Sj')}O=>&D.2 6mCY jV2I04oUqv5>xnOv}{lzBKanq1.%vXgl)Q*"VG)!/ Bt᭷Zp!Gnt]I ,B*2!EޡKAB?$pyqCD ݏWs׳)%TU[!.1%q5ydg3R[\\Yڐ*YdNǙyAtD#ۡ- ӝ+YYRB% C^|X먪Vzqk5m㘑H?r$au5`*u1rע$ FD8(@"jC[iH3 'H@B2N*@B6+YH) c<{S8#vK}kJXNE\ۮv~O yLj40VU- *q^HZ DLY@'YV;*Dc֮SHuƑk6TVށf|Ҳ<8EhcPe04´Lmy?yFNfwxFTՒm%de|Ԯb*Ʋ |tujm (D| HkYGhMk"jsϾ}6o_>/mxH@oѵ7/3{JuJsD<W &2KXUPqI9_gkc@[vridgb pdYfnKِy^%,q 4J:VK)4Z!$f_}ɂ\Hq8B.9SrX>Ez1-!UT n-Rf?'esX]a] SXm*w nP޽7?7فo1 BDpWi_oc)WwZ_OήRÔ",VMP Pe&OT8oª+""NHRz !s,PF6-ON=7 #mAkxb\mo]u"L1JPG~2,eGgKm\k?j<ʪcʵ.ʡeRq)eںrI> ~fOS뚪l:ǴREYQhvUt^'}6O4#\3\զT(q[ɑSv[./0rK2d1<sa^ (&XAe^%alfALqԧ&tw䯀2u?З{뚟? z3a9BhZu䘙Xzr)=dɗ}Ći1R-R;:%;6LXO(ӸBXNP( ˜gDQzF*s/5HV 5iFqy!#*UZRˀn.y| B*l@3m"V 2lq`N jHA҅w?>t[?so=?-Ǟ>NXx^6;{x @_.K{/F򙯽~tsEwRo-ya14M4"I)g !8lH!&n[Z?T( vJ;9+@V *+iT6}]AFKߏt&ոtsm:*k1%ώ'r,@Ȝ2BĂ%@ջR. 21Z L"9b߿[G >4]P3C5j=Ty\V'[aYuڵD+58LfS8#DzU|8#ԓ\w6# 8rh)1<Vg_ D2'@+HZIS08R3~LHI+$t(!(t0"SD``۸AekY OO$hEXh%qbR ?c?i ~u\n67_oV*2;n ig6m9k4AjG$b2RY8 !/l6gvu lWX|t<)nG! U2cZIm+m!zT\_04/d!Pr>tʃ3uQs'\ T\aچFG&Ue;~B*M*/PBw>Ү@apƱq;~ꋿ@=< OШ+qɫݿzkZ RW4uCca<g\exMe93aF**q,Uba$ C~ aVt he{j8r dLE֫B/ a]ez, s@a8mnh8UKeŠ4XJ)\0Hdư + Jjb;{G2}D+kZ. E sG3Z:Fi^gh;dz)Q(.C>{5q; /H"mc-K,g_<E1mGm0p=:R"!yosl+:~>5o؟RXVd)§t^GTo,2Ux?>rh!0ps=gq-ʢBEkdO mU S`I%y^fYFޓTDGE"pKY-./4kN5kWBʂ%F>T[H)0&dfϓ\םYoB,$HHhɖG[vx_aG?7D=I9A$@ &o%!F UueU{9w֛>d$(SS׎XNeؒYB瑥 0ưhM7,Y|RsMY,xrl$C̉ AS͘LK#-jf3&M 6L,cL~ Q]&;6}`T1eRHGh蒐Yij9l8S*/:Gf%'g[(c":ge=GݿVS(0Q>Q7Y\Z [*@a2`)J @y|NCO2#QxM%9fVm̉a"ő?Pdc b Gz5)SFl VjRIMe%8ԕeӯP(llEU[&(}{]1^/~}Ji#A.?Z_=~}5Y%}ed"2kmc6aP`2msB¨3r$Uhe)bч V(x1mv:G}fՌrJ*!(gdFfFIu{O=`!Q>lڸKHj(0UX)ɁH9 39 :TҊ~%2+9w,`; IDATmF5[iH OuF<>CA KN~ؚN{e'/z+Q%Ġ+W>}l{VQ,7rCUWsjQХ& 瑺f'0_TFagPMV!M"S<X-WtzT ɤ%t#|K_U8)AʳY`mM(R2IW `{ {A Wab4bH~jjRf|A(#u)amͼTMԐbh_"c1`$egu,޲w#8K"Q|*d k$:mWl aݨ؇8z0g18M& d#Ƕ$}A!i{$a18n?v2 }/M3L !k$DCf!ւ{_80 lϷ&#}营V96&jK 0uM2dd*T]3qXḇMCс`g.v,AYz@3\.hx ͿpǾϯS)n')IHRR$7f!LKzbsk ڌ!R7c)ƀRS`0i&ԏ#2W)tD *PH9bt W;rɄ~r_h6K,92g/~uv))ytUꆒжhY1u!n([Tc1RǦO Ek/b(a}ndgfjpLIu6]դY,J@FI0D ٌsڲnQխ}f|!@8ġtz%啲\\ 1iJX3EUU.!JatlV>9g^:u~iͽQh>&r~;n(r|v=9&Uak )YCHʠ$HpX%P27 )Z rh'UQ;ljv^ul̷t]Kk%u\]#ē֎̙3\<\bF(jUS娽?=3Ͽ3'Oҫg9{qI>Ƨ>;^*|HQM&t+B*@L`c_ )& )^:rdL nfz _KO>tgTαYxJ~UJ72oΜ-e|VZK Y=4 zQMͮňKVB-fr=*gC(~4sɂ Z(HO60j͒MRT@vS׬`0"ٽff)oS|4Ƙ]bF9gd 󺢩a`mGKCcx_zxW>RE_q?|SO={Y֔"PmrIJڐQ6VˎLm ΂P#pzlJ,3wh,".C: %DBl%PM5).\H,j:cyzTՂJkjShm[ ( C r͹ <>Ͽ|a>r=i~S]kJi@ ) N)&Rf PJ9`'e:npa9/ә%#].mcW@H*(;:Hݐ}=ŌwCj(BeZg;I?.7I|Rޚ!k2VdoG03)dЁ$rɪ]ڀ҂$/zm >vc?qoOfAWgWkq~ޛO;[3J_ėb2GȪԦBHr}Gt޴{?6jw ڎ,/޹=BX=! 1J#EiyECf,b`Gp52(Йa`}lmԅj}eT9RY2܈yЅ g}BLQZ)\3ELJG5!L]VѼIE~[dO| ,}gݡ#L kH9\ېB) b4 dѣMX(2Fq ];#3MUb3"(Qц1c0Zq]jCG" ^g_+y3\_qt<uT%G2eֲU7$"u@AbplwlzF+əhnlR:>ů͏qMtiZQŕy({ĭ9 &}x"S-8w ٱ|qib:.Io󽧟޻n{O}|=!K,TPa`f"ɩ'G((FR$ٮkkbŁ̂ %/kb _9j `^y`}k[K$1ېRj%VEmn3 t-~䀞vg 5]aQOpx Êa{ 5^;Sϼ>ɳ'_?o,fs7}ףra9,E'r"[1vuF0iI(c;ʘK! Aڎ| y?{ݴ=O GfX-uVO'^oW\u[L2IyvA*n% CxאJsVK~vB+KTbA=1MYlQY(1K㮁sJp_D M vJLRrO^Rh1VFYM4tnaL51 >@߂1h=Ŝſ|G.\Gig^7zvbb\M{Q h{8~#ei4^PA"Zbn[sC=OH-ik4Ե%%DYym /韼O_9K`8=㾻o/}axN&!و6! M; Z&ԅ.Izj4H!A(rn98NC/s|oo# Cz_w|zMhmm7覾SqLO*\bKf9D=9t=2`E=Gׅvu+'Wd{kʣϧ?0|.&A&[^_r`:rMXk*S C!zv9|g$BmB? ևh[QUn"IJ ň Ξ[_<_Zbb^firx[OO dhpE(/9Dx$ziCK3P֔&0tXZ1zIp"g3Nv>wOaۈcS>z7r|JU9b\<Hb4rPTZM~Hh[%h1:XZO0F$A_ M=a)Fܤ{O=G$׏C9G'zZ7qX]\c[SMh! IRjA#q`3Dg ?x-׳}?=My_gb>֛w 6~@j9q+ZcY2éL~ߚނP ٱQ"fPRY2BYDlM6!Fr& |SR R(!QڠVqdpM%rՋH"e0jicH)Ł6$%% sdѵQ%BPF˦ "*^9 7Ϝ_˧ o'n>?gk6ChM.t`D y)%kbCWзܳ51=(2Y#I2-p~W_67txՓQ@2ǽwyl@5'l6]?:~u*Qp^lqlm73eX(~a2)浭۞/)s-|?w\g~3{ZVS1Zѭ? R7t?K<ȒXB&LJhɐ2Z1\bvi0rI!F V %] 8jſ &8htGВ0Z/D! =FiB k*D)ġǷt:V5֫i1bo_./̦n-sa>A~S$)E۞ew@ [[[H9DL1g(n@5(g R;҆DeT$w@|JR^_++5Z }u y-dpI} {Pl>9d.lMpb2' s?~?y[{o}E>##r.Vqk %fU#ZA<$QX)1X֘z:l3j@XMm-N ¥V[~YpVrrk qG"O]ڱbT`z  m$3 GC.dj24ME*rGm:Ν_aBH?"'3gOeFٴbFUHYuaj̎z! 017dm =֫R$f]V6ky;zm/g?ı_=V&|#ݙ߻i8yU c2U=#dbdiΦH3kZZq#<ij?y3|篾#c';n3?nNkLf2BHW(0 %g;7c@hIe, X-Pl3EDcsm̉@}z㩍@ 8@:~u.F3ܿztO>kxZjAߵfB(?&$C=K0eq9"lE=er,vhWΟc3dJw߹bjo^1#УZd 1pf"YV2KrqQ(i0rH31g9 3bGĄP6~3w9}{.g_|WN_.Cx9٩s?){/{n; U巴 ڢS7(>PJDQ]xh[t6Ro%ƒB0,@%q)G6G܄F>s=fm%&2౿>(vSF0O(+."E+$GJ9cFIL" Bin>~+Mzoye͚#H%%)#{{O?w9k4JH#~hie&`g19:YSuobO=bddقRF˳ahuko'Xk?zq2F_Y\9*BL~n<2x9~)ǷA+|L%H: eLDhj-2TaS9B#~EQ 𽧞=?~yZG݁^o6˴QxY=7=d&[5>J &RQ/zC9cM? >bMخy?ɟWŽw~gG/⹟E#n @JG2ac +1c(^,7?C@W5V)($u>v$%0dh7]ϐ J"y@F__ggu}s PSh]hہ:j[o[dZJ %%F~=RW+o||i@R浳I"0A! d{FlhQ(EH-:3 =J( "J`Ja#9JS9KΣqW]\j؀W.|,pIES!IH26}wLfsrpE۵*е>clOcԅ`>i>ө_g^7kj'\mX8=VQC%{J9F]BHyz@ 6}K-dr}fs@5cb~g^x76pY^ CSȔtk6+MIh>cu \O8}ȬZ45Ab̬ }uۘR55)EiF(GNj UT,95 &!)g_w.7~V pZln__~?et)(iSK[Ӈ|sJ @5MU# |#{{{u]3],HRQ|G][ܬbV̜s!^9}/|_+?:ͻ;n(%8mPLI}Iưf%Jc0 =!( Hdݯ'm5 IDAT!Ӊ-hKtE/ݾz,IkӭB:H]Q/mYe%EXf/∾Sϐ$>H;܃(vsԮKi]B{95rTZAJ2,eIKD'Hi1-)iB8PuHA 1VgOjrl]YDmfGMoL|o١C΢i_uZb>@o]_k\IzsxMe(i4o!&B=}?cb1)ќ# \US t]]Z;h#֣6RV,fM I)_z!}O݁R }i~S9Fr)Z SAit9>ؚcmAE]3 RvT NRdȂR Bfug/\Ɣw>D\&gbXX@ V̧jhF~Ń霦)!MؐbDIFRm0ՔNቧ^ `ǿ.x> *YjNzySqlB%]$2I JB`"B &2ʀҒFLe-abDDi*)/|rw+A{T9rx30fxeZFP"BVRPRpSPb >2jeD[UUENsѣ}f4Sbh _ )Q5Ịk8uF7/~'2kS!$B7n q5YX"!j2i dk0Q(F=#m!sn%!^|L.~޻X &#hӂt)W 2A0ɓ<b>b H!Ci7Ã)Sv$ێ??dw8Г d"gctݿǁbY"%`Z)A gZm36!@pi;0u2ulSAUɨڙŃ)Q:g92kEK.~[o% "@vp է2r =mKAW )@[CETf䮣o;![|:%:G7, T(Kb}1 ?pr.|O}q.CXw  J"ʲ:8rU̚ |+]߃ -"J>s'8"zƝ6G-(cXG=n2g%22} h\!0Q$zr83!* F RBK*9Uh3CY%o0'4ZC@:C@H0kICK}F+74FpF Wlٿyo#QcK]{j,FhֶO8FQR(1q=P 7m(B:Z1MzN{Ŕ/ N'/ȿsb %$sRDQ1_Q@vN wv;}( ~ k Q*rlAm*o ewq2>RSq[ E,\mzGpf'o)7~>p'6RDFHɼZR5ah! ˦ϸjȂUZӪBJ[bpRP7lE HI UבRHȒ۫}ԶQQ+8Ʊ^>UM֡J!&O0=f5xCՒHUqn@)K,( phYJ|7!Z+jS3/F~{S/eDê'QQ pO=>9 (q&#A0GO(kiFXAS5|[O3Դ BՕC)z"倚_Wx֛Zu( >FjF̨ $ R,+go2+1(`'U f>3/h?ܧ`ȁjM~HI JD36wxsԮ->Mj4>uŖVhg)е]̧ RbP $Fe^;/ڕKdןW- /ND$Pʢ$A E@h~cH.B$Ql! )hC %!t5t,ICο(>w'ynGȊd֤FX"t>ہ@?Y3VFH=:dF4)e|X"?~%]vWɇ_/"'+%+?]|/7=eqRLWd֢fX2ᚰI"Ȏ a`}jd] q8kr="U5ފ 7g}\F_ˤS֐D./US ژٶ,h9:zЦ #%A]i2"$1"~p@=E@rA:SW֮]'<)'kem1JSvY9zk)cz ]A:ed0 EMTtT26?+ز}qnnFd&H @)HEqj4q5)ُ.z<55UT3VAR )$D l NK~Ž:u9}w^Zϖ5G1*yG-p^9?x]my"\%"k*zlfYF DtDvw'46xccqxT//COap?ᗃ?aßL)--V+6^m;ty1$1&ZYKLڎ 1Fk:)F9Uy>şCk7wwk9w,9(1H U8s" R&s8ɭ,DNHmQ6 A0]ZG@1-WhMMVcy .[;{>,1MC5}KԸEJC甙A@"`E֘Ȳ#./H)I{35 ˶& rPFPrll j2fcFL-%yDo/؇oѵ58Wpj> 5Oo<) "Cff?|nOӴr`Nv?ptuҊb\d!RFhà-18B5&S"$EU u#%&Y<7cYM+ſ݁ )5I*p.z  AAֶi9E B*V)!{)E5$R;zrRv_}~q=4tn{̲k–.xb\Xrw$ Dw^מA MSӻy!VGAMHQx)Y5*zl6L)=w~Sg1;lΏB5T5{|rQHaJMZlP}%-!iɃNľf RڥzW8}^_ 7pny?4X W~~[EDO$/Sld2eS8%!nk\He ںڤBG躖Hhs|L(m1"ዂ"Y3t yT2QZ#GC u&̚]{1ߗ j=(xK8t $%c(;iY)e!bgVQ3# =t#RUJIH\SUSdmuWSfE5=JiLn(J=g 7|] ,QY0^= @lpCH-QZLx7pW"{2HGu:gz4γAjLDЙQRJx19PC;IKھrdF!oO.6Kv^xFw'.6%~G(#R!X,[|$5RZRc#Zej,IcM] "@h->z?_͙Li<vogpP; .'߼,.K@⊰tM(qG MUdy vdhX[\ǀNQYh\KѤrCN9z7| qAVe%*^~v="0gX{/'o>n`eR{neٷ(Xa (S``s{FȔacc=Z ֎LVFDF+O(Kn.Kudu؈vQS%x(HbɌBIBbRij͒"hJ99)!G1Tr`zVfXm2јlDmڒ[Y_:kqw0DqXm)ady.cMY A'waw .iG (P:4Z0]=QE&GaXzCBc88 y3,K͗>q;?N }vV{VԋFLVsYb6gQFl=2d)˜$,ɔW/1n8+ }%(wzwz(CE;x8+c N>ϸA+Tdbݳ]cgw*Ͳe9_Rrƅv_9.:zɌᥓ/_ҕR_#MݑR`RvAD6,!v$2JUSmQY!14MA)8HE5bRdDȵEۑR" Jk0DLɨ#x6?q /w偧N yYw"/|[?x`4*Bu-֬.2x)H)# ᒠ(*cj !X2E IDU^.qkq;?<^Mc9CKEz0e-[y:؜-9~Q޳3_PfkD$LS5騛9r ȈfL5gyB{?έᅥ)fm}JQ]Gt]Cz2gs=.ޡ|N"b(A9=Cn$Au JF:,KD #E$>!+F7Y "_1gگHyHdcJ#B 6ϩ̩FY6@bIi̳ȡKs뉡C Kv.<#3@[ F` <[ IvvkB|k8y6]O$7pQVm0_66Ú]Rvi J*Em4)%xB Fbtbb1g;kL!rw%8}~k[w?A 3$d ,sVJB"mѶ,2Qg!S IDATɣLO6F?ҺZJBLj SY]2'D(sD|\@r~I4ӷcOęwT|S'8[g}(vAvnL)ʖZ^#ňQx'.VWFhY,x=ͲEO6* 6%~8uǟe,oM/;P[,jd(VF5PAš.$.B$\u_wm*A0rOOow2t ǯ`[ӵ-3աLJ(&1QI"C]$ZcH9Yjs6wg,%6/MƐ$,LD(BUn&ÀJr_~b~;(?1\.> Aoᇥy D#E"T h-ľGIArt\ۓs8SцcQsLT$ q!3"+Νc+g^ﻞSGҖhz 02cz'NRApDQ eu4!) "ꔩI*B`[m\M^#O]VJK380ʐ<&S/9~UDASYly&挬̐J#zG+9\v|qG]qI5r"E@_)r#6h"Ѻ "ӄ%Zg\u*5 }Ia KM(DiZGBk=ִ0Ơ=(JkwpX}axC ѭY"D%J% !yTf"ѧD ?%&K(#-\!Iآ16'hn<;XX2,EQt[[,H2 TI>ዟGxo)o7%g5Ym=h]C - @pa`0/@/w. lʌ?qob^qnJ ľqkDF9Z*rAt!ȵG)s\@ &-N  @ ORR,)Y{)IB~poAO]o Nr+mDzt˞"yBh悔Xt) .4[tJֱɑ Q +Ia 87t]Gאn싧76w)~\u(TX3ʏm|q~g'RE/$)Sx+4[bd-EdV{pN ɗ7Nߖ`k {뒖e?|?,cD# ]9'D4R \X,(d(vfs8s_;[; !^Z|0RD!JDQƠ!f9>۷MHEdOpCj3(mp!#)݂b4> ѳX.i5m'LJ Zk"m;~Def}DUUFD0"FŎ$!*@p΃ln98E3ѣzRhlj4s`>wx^PRK/߁\d\'.k2c}Tbe@SKV(UFYZRNbnJw"Hy%8SρWy|! uc3g{s<+)GtM/9@1+BD@燏>˷e-7˟(( mZB4}G3\$%R LRA5SeEVXY=J9BvhsdFb"&$ih#30 %YQN-\R#WVIYzΟOŽPA{a`|e%0(dt]˲HVcJP9,D""$X)H< x.t-bv`lF9C`ٵ4kcƓ11D|/ ČWuw~wCE/2JY"*ɱZ|=Ź{/ڵV*B D&)H*CGg9V+|K"s䯿㎡yuPZ28K֛9wibG9j)Uupj1Jcb,UYշe3k7xR+V&,c2=F:PUY'%j2-;HY?v)J#q]OP$X(x A-I 1ڢ&64}O@ d^{ozsOzJ$ĘX J:8f0GQv Viw-WִrYN0N`󊾫[(@>r!ou~r6?zЍ<ϯg~#?'>zDH}0=jSgũ*sx &J)h?l\}ݑGUbٶ F4+@Bps\s(MYVlՍcL1u$G/z9%P8_΁?ȑD#I$ % BRTQ2v )K(Rˬcٝ%@x(B$$I "#DsVR EBPTS\P'+K[Rͷ~:h pD >e+H+dQ4TE$if IȩYtNBDဪE_4tMK5vD'0bje3THYVdeQ;DPf bVӐB0R`,zP"pop"oR?)wA"k;a69Bg%&7"8Jy!l,L 5L/zvD#ȫ!r ^;E EhT`ʂÏ~K'8@U{Wx] &#rFf Mх-fD{~'MH_nI(J\9EeS;y#gf?Fc'O;UC-V7-; :\LN$zHE\tL~̋x~n tY,Yք%Ʉ#4͂- <#]Mkr *`+Қ@ zJQ!3{ur"D4GJ]d=!4̗-Mɧ_;?D۬\QBB(]!Db1F \5!ihi}B$HԷl-挳D5;eU:˜^d2#8snaudEF e Dw\}t_cx ҕ 8ã?9]w}#z.2ѹBIh[UDcE76BFU3ۻC]LW x/xԹ76Sm<zmA<xXkXֻHUT$CLrtc٢e{g& 1~ w{oz=FE Q-&+iee=Fp /s=*4#},:D2c^ $LackkFQ)6 (i$5E'@(rV~<.Up*+!zO$Qg/>࡟l= 5އpuXRi<< z`e2HmZג"YQqZw{mʺu[YYb[v,]xBZ:դ Mg}t"<힢( ӣhrhRۜ'nIӷ(Oجs=P#\!rC.))Jk 0DANWg[pOi3F+SrA_ Ȍ&GUFԄY8_c s: xqEUX-v-%( AQWQ"#u modՈ,]* [}Ěݷ}:7n?;~ t~pN}HP*؆3Rq>mϝxu/>+wݞszTz[I_{9L ^"g:=V'Lƫų__Z^yUH5Ɠ F z:6 HBjI)S"8&HCvl4%> MN Tn >BQVTyEn5 בEud,l)[ ,:1L&/|}޴ 7x@t6'6f{?]*{zA<(#}#xH,D߲!%*Gal&PIuHںÇ蛞bh*FPYQc?eޭ}'򁛏cf[IbG,[8v3fN}pAwrbItwg҂nqs+냞bL;Bh#yO}FnqV&v^S;y5"ivv0ZQ&li 2ܮY%kZ(2" r`ggi %=.&ƙA)׎ %TmYb.rzB}?%/2+]KyEY]YŘ+2}rF]Ӯ|׾"]RN |/~9Fgtia.~m!Eb<*Pл@:dV!@ܨG$i!aX撼,b:]aq.WAY6ܬss9pw?;5xpŎ&Bw=)vng~ޱ̤(ss1azGuFkXc8n(ДEA@k0H;dRX('i5)JHQծDa@g~!qh8d>`<(! !Y&NFEw,z, y7)}] ~]-p&奋"/(YFJuQ%EY \ JEEZd 谼0F'q^)r0b-tR0LSaXMߜ*U<%HrrƃO]aþċ|gؽky<񢴶F:2 ~hAש ]}ʺ9_y +ޑw|/-i$"=I" OFf.u欥9p0qwXyVWaXoۅ1:ѐc$q"nD])꫿(Č<Ð~%a@0OeeΩ8O9Pް񪋮)f[x eUꃖ'/ 8yY& IDAT2,-Taqe )wK)K% qkuzBd2! [o1 O+'}#.\eŧ _uY^\xmvn'>x+:d+,%%Uhc1\Ij"84$@)1` wc4XgmmnOA)%e0bR 3+SzAh5*(3C-qGN)=^z['بS6yDomlshS\~eńe:vI/A;$+( H~Y9(=P(fR [x0[)'`V+;bBQtg,)R Ef1¡@ B2G Gh89dynWZ|Q=ItzIJ R2CKRQ8 R)> |}_nN;Ca e,[yUYcVb%gh%d3^'>֛eaQlWN!^2d;ϰ;H>@9 ׍XHa¢F])ƋOX];R!nN;Ka A4N5r2F2H"$yM2>7~K֙ڶ=s@vax@a|i8WxEAVP$B\0PxRd$#n@ ƤQ5UGj7g]U $I$@A:!*NL= fmwmzm}&;c,؀Ҕ!_{tm'/ٵ=Z6 I((eE$ah2DSxE$8$ϱYFTU=]sJbo, SHV D bPfQHʣF1~om&7 m,7^疗z?#e<\g૑qq,ҁR1: 8,-blY^\II$qܡ$8/s!dR2dXGa TɍCMuJ/P!)댭3xě7aZoMA٪`dV-*4izf8ФK}sdc&EgynnY”$'dxoIs:Yy, ͈nB-14IzKU!×L=aDQՄ6:yAZhU"٬d8\c0#N"zR)qJ<?w_Ƭ1oo81\pjM  \A-8!btRlsUV gB!Xāz s,( QH'Rְ:crC7 ;=oy bӳh| J¢TҌxCGG>ullmڕ~+VWY9^]\D0 Q5ϦLgG|G&lvyY\\RqM"ABN'=LQO"@*Ha 4( (!1EI_|/̠T&aVF}:/4 豽@IKK㈢5I?:F xDDwpր'9.%" : &C(YJC/t<2\ A1=VWkյ|İϟ爱βo,Wg2hCmau~mK]0bͺ9=HYL*=‘ǟ9p&7~WVG,-.)c=I( {M N>[T\4z:edFj60 U]~'$+&hqb qHX2!"XL_k@A&HH<(ϙD/,ИDHwG~~߽{ǟ=>J?]~|n*.c634,RW~j۲7Os.<u>Ծcz&ږ؛&`sަՎ5{L n,޸Ǎf@jM c䓌t<˜t" +1+XX#@:-PmXgӜ{?Z::rXYZ75|Mk|X6mXomJrVm\Q5cc"Zué=੿}}]7=[]ڝsxB$+K o 9yz.G8O`@*:.*Uxk&r$7{R(DUNJE^d}[6,9j 4QZi-@+xW0 Q3s`87tB6$BЊ”0Ftz vm_sRT>Ӳ=Kg32Kۄό65ʋ:ݵ+T7({N_޽4na~ǮL l2ݹE,dcI!8o7m{s7ߕ9@mLsuT!olOcӍbwHi;$Djq;g6,u3oitMJ@׏ùLpǽןHpR'tLMTBn3(- tLRYY'?蘍s*g6?ݮ }Sڼ,{rV̓ywߺQ*S~m?ֵj& 4\WpsQ9;&B% L^ 0ƒʢ`4pZIdGdR`E^\މwkfH| ml 8+WZ?V!sP8M6GIMĠpދQ^SkO;?S@L%{zkM[h7)¶yߦ[)jMZMm%:Um{z~j݃h,?]sb=񎫷-E1HGJx/H!:ޖ,.malZqY&'KGSxOYZV/}Q dqlmvʿ~<ˁmJ_W6,<6d5=8k߼4io-_~ w=7ۏu_ti?J|^Rc!:g"dN`ut=X#,䦚6Jz|i)LbKgp;5KrpK>u\` g_O?)6րxyQYOQ4bٺm7J~n{UtrmG]:ۻB:6/$(iqXq{SUٶD'.*f.˿Sgm譣4@7.+SY [4=8yb'}n^!N"[&g8g)d7(I}ky͒ciӃYa3?Æ<+η #=wx7sXPorf{g=FjFajxbMc3df4Ay/Bt#! :$"B`p%Wܯ< /w\ lt$nDjuGc ΂MLJz#;t^=A&z2Z!҉bt2Iӂ €p?߮+,/aPSFf,P7gh"s?>@u>HSDR΂0TY7׽~7-3 ^]ÓdUxOJ'G Q,pB$x[oמ^]{?ec'9cu xMs: {Сn77Tb#1^@Q1QdFKewp6,#yYM7CmFs7{f@}Qs‘)Jp6ԙ" άs/r, IDAT=a72d#H0( oIMѦR΂m򶙼3$`0IZ|ӣ4c28וJJg9 p:||ژٽNu~ܥ?H|@~3!O[\X_'bϊ^㽦,ߌ ,ȯ>v[VK c#2(RDZ8WS0&Bo>o?Ҝ͹4DS^r֯߆D9m2qABmmp-hB G5_n'o'W/hS6fJ4!f s7[?ѷj-q# ckK3DqLv$V0&b(FI+J,ʈ ѼkbMtwЛO^{pj}(&dɘf }mBgG< TG&\|5 u3cw;/~_{ۛ^UKK;Jj 6p>I6VogZ`,k{j;;1fb: ySֳ| \mp= ^ted0)SP%z](`A:A^%2WtJÞKZQ^0ʀxh#GO𥻿ۼ<nv-/}ҋ?ȓ/ um}ڦۦnSua_~zΛ~rܶ1(nf놤M&ꎢM6 onzVY6Yh4Z6/$dmMɛT  L?wkUiDt(G'V3,, \ m,()(eO}yPx2K0I%x%~% uˍWs/wFYFSfDL †>>OyIY9n:Mۖ5eM[.o޹}i?gN, hMk f ѦX),$ܟmz,oŬcl |3BsaN9rPR(OF`5}`YZ֕MemՙGjzj VLrRMU ?h)Hu MJImb)94^x Wd3g_=W+\ӵqqB <[=1N}O}ccͪL-\~Ѷ7:_WL?hF՜yd/H4/qH'L(0 9Wڌ<|7~ə !mxEPV5-]яm?|=wSO;trvݞ8 K4S0{bFO@zlm~][Σ/~PM.t/<)B D.I)i(J 8 v;Lrd$\qvX&q6<,Jx=kz x5`KFYI,c7V:ΰNyBĽǯt/h_;xtm[*CYyU2M?˱̒ l|]]֔oPPS:#NRzG(5A+(ƖI$].ڱ =Qс$/v<$MSw5DQL:{E"cv,{zeoЏΟX;~YO#Y??fetۀ*UΩրTLʺocF}].uem&hWo+j;s>֋BEG}5vqgQGi3(#c ;K-}em8ؓZ3g~ z c(E Nx^]:&9 ̇ 7Y;ثWX=?oyi ଲ7CfJ(M1Uief :Bm&% {{C@ H [:R[j"SVsu:oEy: s,Keā|o3O4x_M ^HE Sb% #OZ,ݾ0=/-\/U,uҬ4T\,^os18 ,s;_؇v6s_+}_ԝX8>,K<":,u=V? _wu# u,v"Gٍ٘- OQ$%B @ X;Ykױ$ZS u΢z6o `<@7 lxe-$BmJKK+!ZYƣ Bx(D J8> OQP56\smÑ`E)wz:.Xc0٨ʪy /,Z "\tn Q s7r^ZσMhDg~dCCFՓݻwV( W\Y""-TeXϹ❼p|o%P< ,r2!뒻x@n)D?!`8:F:6QXrIe8! at(nwް_~a4Pl *2mڝD9vAzvP6J!$B DhŕWJaH5ac\- (|/B Ov<(8=G&`Ǹ4*,ǔX%ޢ( q^r豪틜X2ߏٱ0O.|/-;|of8KX\XDeO٧68o/$-0)\ @!ACijƶBdEblI^dCݸJSzA@Dz@o vޫnVz5r;eh2bR$̤( DDB(>ʙBzqanQEA)rt<]DJؿtK~C_}g;ԉO;ů=pZWi]J/mNOěq[@[`5}9`Vƙ^* kT|JTfpN_uT` J(gN:BdH!]Q2@3C& эcPΡgg$|kXa=BG q7amRki}/]-cmIVǞg]ڬLOuF#VWNFҺ#gcMhl?X"H(d>f4Q Dt=(@#Oֈ Q V1){tI!3vlgAJsŅk{v,򎛮2) rr o'r"8a%[ZKl[c0Z'tJ}J)BPwce%\se睗M<5{ﻏ~8hf/@3oZ!Z~TT8=rh58?uǖ[qΐeg,B*dh9($Bĝ27AU &DB0. )pZ@h_,ֲ}p6&EդI8jJ 5؈ )A,+v 0%$y 8 B@;Tdqhj%EQ"l]]Uw8Þ1yvu=hg}9^[WUW4uɬ&%ɆdeԊ1R m ǫ׮^ˏ}.ͯ?~y@ 4sF*B=g_~;< 7GqB"DPY]bF(MIі f9_%20] zObε˪9bbp 7:~G3ق#1iM 4e[ rlkV' ӄ|d& ]?0 n RShMe," *t1:3_,{{#{*?9 |n 2s =\ $2RW(qʺ"ňsJ(ښ,=a c#9gah˸ݠVĜ%A-a2#s &4yV0k nނ?rĔꊶ]p| _KO4 ~o*286 qhxꩧ~|Z톟-I{#uYl5*"V80aB + ލX#s&{U )Y,,]xqz^()}eQٜbՎHIA25Ec#ʔEK̙Mb4&y@D"g.J1]Qڒfb!j}Jt(0E{g53!&i~C?W|ڍ?s )z7do6O!=d"@H5d[@?v4`6+l#'#A%BJVQcA OΉ!Dl(>RRJMJ;!rNlÄ#.z&PZV M-Pʢ[o؎ۓ5*l}sPE6g_w4/~^{񳏿JFfЭל$%=gFts[} ٧TU7 ٺ=B ÖiPE޼Fe)ȡE(XF;bmظ)e!x/3Z}۹rqvCH>rrnrJ[̂T3]̜^FU.]ȥ q>YZM3c0it['}O{,xڲhjfj#o~7>ۿ}铓bpfR^˼/k^3(OY%RatQ~"e˚`Fqq3Jf=B["EA#iBdc(#QuD mU"e)Z*ސF̷nl‚箿>^@mO={?//=}꽸 |? {ؿ\r|菞Jxc4VI~SYNxvs %1#Zy&).Lˊ6 jՑcd?^.x[s1h~ȅK ʪ ")xDQ%[c4]X(W.߇ 6 BXEKŌ"}.Zle!DlaI\A0IbmF loЏ|_ǿ_[bH+ݒ/6y %~AGՇTBT$$Zdf0M(!8NOOl!1BiM̎w彪z=lDG( *m0ӄ(+b825i*&aKJw(ruj+-Ea~߹O淯!;Y5^d.qcI]v\Rp!H3vгlȺb,KVhfd !P7 BV]z;4UIH3>x YV(eo,yyyyvJruF IDATQJqe"ׯ3o \سcțk+q@ʄ 5>Fk,TܻpMH̖4;2PR!z!$mRQW}W.^8\?Ѓ[>GG_gOnq\~77eƳKT.HI@mnZ.EH2Z$~XcQ0 \޿2C 7o4휺pB#Gi&BHY#lčQ0WaDnv 1FR6$I.޼ 7/HBMn_.+?^u*S!sf} 췿%4/R3.> <ϸ9&EDP -8GIC2%Б'@*H -38XMM~>`D,gn^5z-\:X-L}GTǎa37O9(eqDn1X.[ %XAz7r|%{ǧmG|caj{kSbGRPyOCU }f Rд ZcR0xbRitxi'Lі,fa8#e]buɔxnBV̋%؏?Ͻ.ky[o|ֿ)ν΢ ݝ7k%qI쪉a\ k% %H~JZrȸ p@Q7Z7xis,fKtQ2nא3!%X ۰37ئfvxGLcS\hĔH9!( F*@J SV-UӒb) Np.GRX0:#OFG\\7|)^>>w'q"y!/ mwe4$g݀Ic(g,ah##f ӎs17?@S%5>ۮ!ҭ]g-) RZ1)E./1]]ڻ8llFP`oo5{ϟ̚5:If U ӭzT/ʟ}+|?˫^xWQ03zz_b DS$ƞBdbTq˸1RTsHkW-!Gb-"!XŜnPw^<<^z))%v1\b|*1 @*01D?d hKxZvy7O8zY Kf Sp. =" )ILJiʓQR]H71M,USr\^ʻ:Bي&n{?;A-y<#˺ݸdRLc>.D@RpiF.[ya!ƑAoy̚B |臄( *Y #rq)(#H.1bV,4y-oM-?wp_Qk(J| gaW~wJs"_MӛqI1L!#{"Ѕ*!)H`+MBvȂXWx)/ >oIC"+B\Ԝ״f$vM \M.{GˀSS ʒ ] d{,Q.Izt tc,,UnڠMŢ)PeEɏ,B*I@Ύ!䤨 RF=w$n|m%9~Yn¯ (Q%r Xm(1F2P[6d)ʎ0ZQZ5O‚̤1C O&L ,f-Akϳٮm*,>mVoKd) @n5F 2Rq*R$m-Gz:_J>(90uhh+e%7Ot`JЃ{]3.HHC)[8-x`;$0L|rH;Q4%q[.Xm:~n+ xr@dz^xyp-G()Jx> 34Tq|&߳n6'+eC^ʡ5e]Qꊢd+0Nn<&ܭ2xh)OAA1IQBЅBN[cdR !2.%~gI]2#HL.BqOSU-!e67㦑YaVmO4cIE`џ"1D&A" qIO B*(v$#bp>RY)%V5R-K8:A tD{ٜmYS..q|o3JU5Iԍfl<6hS|ȋM${ߺwp񢑆):R6V#00l&TeEV$D9Ŕ3.^-a 3tc2F J5hC%+\

%5rU$=fyy)ν]l1>ǰFgKbwBFjlakج0Mx|~ O=W|GNcd];C-GRfGe@?$ $Aם2m EfF`t Zb6'ZS!B1Eŋzݔ|?8ħ,9LF3V %]]`*>.e32IqMr٬f>NiiM[.MGM4uivɏA#-,Zig|D96Q*=nЭ"̵7C|1pV9"}W-HgYhc<4GeI.dhvȶm"DR@iq1 +2AiLƘDO0q!4 { ''f0MpȈQRQ >} E<ʧ>4)*e͠F5EFjm[M&|8؛]F4 .B O@,9)@i-3t"LU"/)ᜧ]Z7#[boRT dCW!$G[ʟ~<7ypS"L JD70%R!DсB$`H#9 r(VR }@3VSU0XAN'brBJE%}4#i7;jR"r&]UI)Rh(mC$R(@P4fOIa" B"< ݦ`-?Or7 WlW0H)qD 0 ㎰#UU(V+\H8QR4I9\`rS*N5ZjLYGHa10IK8蝤PuvkpxW]δEX-W[#-]e;O7u3񋟢[|Ւ-I<ߝ݈P%5?׿>O3gEώBķ=I^3&`d2$rѰZFH䉜":TH]Q63R@7PI`;DdΔ3Ŷ9[Tl${E451'r\+6S~V״9:Y#s$9zcD2nZ#BWnP Kd(I#2Q8QUeY?BٚrV1KRDGUW19z0Fh)j|!ErJh";BPJbLQ6?ϱ3wSr; xqB| ˡDS q ( Yd@,PJ7o=7K LH)rPJ l-cp)LMYXD1BƀњDj[*pcG C*źbJ(B܈%$ ,_>˟0?p-[!lQor?7OB|8y٩3/I[4_ߣɰM8;R(ʙM\2I?^HJQ( ~RG XՒ@QTJ%lSR΃,snP١dQ* s~Bro4+u(H" IzRFIBFNUx۱g]B%{/ !b;R lз=*)J"@1ޑDq1=I)!O|:;ÿ܉8p,O UiRXcհB9IYpcIzO@X2l{ʱ.":@ x%̵eF|Q#`<~;r>p2Vv?yn` 6ɥa԰} ććb@)!yS- ?N<9NMSm-q~ﻷ/BHZEHM- aqѱF|c(#m?E%BK7Ď4;D,P$4LNpEMp<'`~E,A~C5~;rYm4|57$ZR%{ZsRУ$ {v) KHҢjWF5" $Ȣ@j<$rť̰P%c !O/s>fH%&4BB E4FETGMU  g3r2 *DINRrSw< -o{?+Zw=|/dl!vsCm+ ą"%4!$2'!@t9iJB]!GɆ9.qd{Ê LD>BƹL;HrYF1ێ){mCSH)oA А! j!q ;ư{q+2?ӕ|ozve[^;, H;nl$ a7'OJ Ua Ƅ, Wc‚""j.}1bi[J~3m*CD:؛Il!bnJl\$DD1r|[?ǙW]6t%Vnd f6Jǟ'oSۻn7{iohf@Qi5Kt Id4tSb9n$)FVX[lB!E4ҍmf0vgy Y("c{}FwqGO+o!4D4‘!=.WD6x0@ BCL;sL jT2eY2(k1`;y*+981Lq7("Le"M ʨ­M5(fU)Er S$*-ZvܵOs?w0̝lQ9BMx~i)*':5CPJq0˚~L#Bi7/ f{K'Œ .3ƸkNEUz CJLP,9Ţ)DtztS쒈XlUA =1fH@ 0n)_ʥK~#~c,!bs'D%O/`eb=u\L "Bq"0mg&( e$~]Ypd)F%(>O|`ˊq_/e*ݚA L]OO?,ܹJ@'}\ƑV" 9D1 $J %|t&a\, !+rN[5t=G'7Hrpq-g@0DI3Q=E IpL|GMN6<>tqt$ѫ/$u{Dm'H xQTżө#FUc098tC?8N|k<oďrnزYq!QK&:ڡ=(Ã\$~ld_  %+Cٜn[^fbR-0rFq""VJ(. f XՈ$@wSk ob{1EM$3/++^}+CVzwM+0oⅇ(PYA3ǘxl'v؞D%XDq q7qAp(HH$" N.vƌ{f<{fX=={DOWWZk=?v;3M ?Op<\suy0Bq&Quhv߿7.ůoq_OYUi=D Am(4x!@q.8$~;6YAL9'jx !fƚ1xtYt 䲐+hއ>˜3~~cWBt"V4F\ng<~idz -Rʕ2T26\s:|7T"BI~}wT*1yDC Qmf3RU$4;R3H,F ZUHl zH39*P`e&FZm'>5-Opt".=ņ5dneb&- 1wJ3c6g^Gs?ix;XNgWx1}ulwR*%Ɨ<~9K^?~t0WVc+z$\ FP9TwV֯{V;C 5Dx DcuY9ry+ױ4 RXkx=U8ݰuR!KøۢBIEӂÊpg@rKvW[09 Q*DTƻ}ƛ%hk@4H|H 'pLitIP3F+xF]GLe:3eX %PnS1U$7GH)PLӒȪgƛhO*RJHeHˉ%Ub9. 9/s\?ׯ}|O~̏~߽0Ɠ"Vb&0Ԫ:~SN88a:ӌEdD@4n( s(, ]v?`l e|'g(\/yrf<.BwH`wPc qEx")x;9U=bHy"U;Nas$qN:RdwX5DR !4$D+ Ziz忤Q!Ҩ!%t֣a F9f݈`uÚ[qV %2 ہv`\)eT*9Hh\|8*2O3`mY<8, mc&ʉsU޿w/ꏼ ZCxO4ZXfKi =BhB,!pNeCP)[2/dFJN+ƓLg:aw[DPL RH>tCHX9"rC;VGUeB b.8QS4zv/)t/?򽹭Vi5J\H5doh2/ۧ䶒fR8hrF{=qaI*(lo )4p[23ZidjF\ ÑNoGJX4(W @`TGEiȵQe7\`GhH$NSfI DCF9wqDjURڞƊI6"K͖:LEI )RriCI>, Jd _2˺/./y[Ƈ.P B#R@WVX%g돮O㩒d:$jScB*G^/o'~3pF%cQ,Ց'Oo9igU*;@ʡ yre:qxnQ{J* K3 vD4njh?\^,%nߡL#x 9pzEt=0em/{RLɕ14[͢ʱ'΅ttK,m}sCߝ;z'^]|w{\]νWh00/\x,Aʦ`Mʅ*K-( B[iW&j~PJU&h"Wlf9w/kl9·lhMp Y ;*fӌԚw<| Jɔ)'Zɴ dyF*ü|-9kn|5H@ Nb@X"DL nՅ' A.3s1Ka'-N%1\)4oz=#⟽;яɰPdCm6\VV{"HruFnER{w>:<kW~3w4D %uHUTP ۣ- bYM0cʨxj?))a=`D4&{$y i!hV36+_Ao- tRỎyzLj}dtX%pnþ1y$knA5 ^Q[^+lV $7qE9K*hjAx&Wp}t<&h+X AŦ֮f$tU 8k1rѓp|~~>lbp/ WU%WĿoG MO9Γ̾s{]Ga#Bk"Jfqck,iPi-UM-Nsh-v- ti4eyp5R$J;޻"A: Cuw\m%4F]td ,n pcXfa'58QƓBɉ cRa÷>9/<7>~omVA!ԄhФοNX1dKV(m40)#yVe)0筷>JR!LPrFʗyvt^rfG)ZHdsd:|s'Z4aś(MnNF&\xǻUe9J`:&~ ^ n%,BU2+SD.,2g$k#{ny4c6pMk*ocAQ1C`da \ [+_{{z.~%e3WJߔ)_|}$#jGA*RРL"Ijc'ZaJJtEJӊrUѵ P7OlvNS|~L(ߐۀkYʜhhv[zK"V󌒙A T9' Z7 | B%Fl:ָ:F I*Vi-D#yzz2W% _^)=No$B(: C.# aCI$%j̆ddX]r݆K9LBtrOgz|LcaVqb@gȪ92RqQpaq7=>a9 %ҜR]P[#O&_\Şa(Y"Dڢr}ЬVg {|nl?T#ᴊEt_hDžytSkDk(5B 97 scfp1B#}D?[KD2 wbi@F !C@y6k668&r~Z\Ak'7xѡw Xgʩ$l8RIRJe/h9bS_>74DıU g p&b J^ZXaJX-ۃ$N$p8ѩRus w\lrH1BSJkĒf\|W s;o1Ql+yڲ0mOg`xX @34h QͳLDgQd$ ;1x\34kv yEOyf"^HKzBˑ"Vk_c`{h4ܞniZVjd0Z㷿s<V{{}L g7X%'DˁP2R)h=Kֶ*,gB\IjZQUڭ>xtf3.]jLe G|wO&$_M!yC21=Z~R-7 6R ܄D́%Fr<=kb'#Pk9TE"|x\^ "2,/֗>_W^:ovQx@ibGBIB g+{,8kZ+V!Mil+gÇp$jͅvY>%pi^msDMcje<ۡ@mVyyf\{LD8׳|8XƌdRm?oDUhcTRB'79-Gn'݀h@(qXIOOM:%nʉR ʲb )3V ںѝc5*IJg,Fŝ՝hȄx: %-UfJիO5<}@peǫF 9o:UW;T2SQ+3*&+Kb6h)ȥ ScuP)Bb ^auw+<4a9HdIæ IDATllt! - hcQdqf1amFZ#R6B'II swN1b&~O~OO<Қj{k *u$sH% 6c9ORLHaVu! ҚI3T92 J>gygWGKd^ͺ8@5hȭLQelpz=%RA_ҹrc loٙCw[#O3JJ3աe#H$qΔBU0`3-R*+|YInV-C-=ejlfH@ @7Q8ck'a~-/s~!c@S-5JU`U42xQZD|Rd"!Zk<2˜5s98McW>'D^Fnؐ5a=2C(Rpn6=[C+GYR,d haj *| nhV3UZ:6PMB+rnT*S25vh)Z2$+xH r*L !&JKR^ H ,'! DA & y"/ 7x<{=FxyPxϞOΉ6"@)"rdyB[Qzlt~z*CIL3|ɩ`Pf-<33uJXm1pL#3()aN<B4΁J4т5aqL,fn@XDto(Kg5Bw "ep>}+o<_⽌!_&{vRZ^YW-yRZԆݽ݀jp^8&5 0}&€^Q~aqYgNs"80;9kG-9  +)ia |h<%NM=M.h)5(0tZQt@(հ%*ydAj)|#uۧi8(]W%Tֲ>m%R*LJE)E h2gDJhVoۑz/oIL_!rHdXV+P3e$7͎nttiyuLئ8zy%.u!EXMz|Lak1&*+nH ZXY$NLPZK@.FA16[ȂmsZRj24$B*uNHgR }3~wX׹+/B׹˿oIT :zm!rZ|ZYˌ& nTR$Ut\*Bȴ2-FQzEUT֭M;R y 5#bM'GZY'C)&R:u8?`d:,%enϧU1FjYvn3:l9* -A.mF̞!ZtmU*U9k1% ylcU\/%.HpZB[6%QA JV J ~XȴHqR1o?_ k}gϾ-6R# Uy e7@Y8r'JS % /,tPEJb#}Σ-|(Ig74\lՆH0@+ҡ}OPR3.RH#zMS^IN"*rrJ'%\iM|-^(h4DIJJjvN3y9koޭx_\'>>OT=òr(jAvMgE#ZD~U o6P AͨXhRЄֺ2< J%BVnly&̉2FiQV!gRP}߈,'*X$d0beOq"hMBb'T"Igub0Df4c^i|&M9cOK e$j>a@֎88q!TA3WMGmV{7*e+)c`e8- )s|'"/. /ŎW.BvnYj<(f%%4Ғ(VI BN;b=??RjG7)5QTL)oRp{9nXDHaUjKF"dCHTciѨi,ڮnBD8\辣4Ho$q$v:7ί8]_x㭇W8~s$BJkGH WT輦hT `:Ɠ H]s"h7=I9t!9  ~hfK4bcW,b5?H!ef J_)q_&bƉt&%4RX4@C FA- J621X}RZ9S shɎ<F./4R,q:[q1B@n!Zr= |G aAAA~y~uYn~RZVom{efe)R%SK*,K\ {^[|}?5TYW8ZibH7lԔkd)^]ϒy9Lg%}7e,sZ&C 3Y;0 S` êm2AH s j!8-D+T!ɥ@R ZAO!H- WݻSJwOi / .KM0=Q)Q̼++kP41 Vs$%i X 4yIP!j~s`rZIB(IS0r[:몾S+ q7|RHL+ KәXEJRH)Bz k TJkSRY殟UXq:q[:gH5@xF( ШD*qYi^ӛC曃N?{ kiZ jl(#B+ Aj$fx#B˧~{ueOoָXu5J)N#5#퐐~@hMؑSh(-&r =]^qgs 8:Ӛaw1@kh69^6Mex:J%:B;Vo?Bi8&9-|N%ŋR??л6Dۆ3frH+Ac"U=j-tVT pF[PKTv'Ib5(K!+OqdͤZB!;Mb c-R@]@inN'yBd4z`1Ry-Gk"sXHi"C,Ks=2&8 [Ǡ=B)벢2 -@Fm&5Z &8@M }~a ~= M͕WA )+E=Hm5&DZflg=W 34?iί8ϯ?wo\RD@]8/56)QBc3q:s#Q7$K~gꥺ{YHg8,Ҥlʄ`a a E dʰ$(5L E֐p=rofFYpnDEe{@ %Ϸmnͺc뚺-yjZӮGZ ` T+촔BbˏuҶ"R5EC`[O0R$h]55HTh4m,y[XCLEOBhWk҈JRD)sϥckstz5tp̜6n"CIn*-e.ԋ9&d}{3zB!OJ%DqR#0hd"P>,S[FHBL@[^sp7ǻr.FLU1,S[Dt#zVO@ hp"1㩛rght} H-1R0YRH|PhEq.Ph9bg:k]OеF|1d=aĽ*Owt#Sân(bgڑ} Lke7=tERm[>{;H?}p؎ ;~]x kT=a"f,Vt=%{)},f@ DΑ.r|ϞL0K:S@&(>!{dLNQ)[[rx}TUѲKj"zԊRUM]O!|Oo1;$T9SCBJL4p]) e9Qԓ9zF$RKBb{(ђĊߺͽ\8o*)Q(J یˎ}S3 %Ō/jVR=*m!aK5LAE‡DK\@sit MB$D"Ɩ1z-P9 !'?}ڵkʇ~}/?t9("DXifRJL3mM<]ħRuynD)9tE] U Q1͔"9HgPk*M\STʥ 2Q!ց#kRȠu ψJ HY@A` u򸮧]Wx_cv_C[ъIcԵΦ@buLk h)HY8a܂snжKBһub1(.fڙRuz\RBCIEDcW, YMSx8LmUiq>!l%NpNӿ186?o&D\ԨDJI/39KLL%{gA,9wfb&6o"e̽wRLLk7H!:&M "{`2 9m6/yJ,e%򐘏j_.9|[)HG&"W:r3uwn/ot`~sOȍ<cEd%it_|]ӪM%GOGdPͽ$2i&zG⨞݄z.PeheV1"1BЯ;x]VR +DQB̻ܽ{]MM'TJiӰ?Ys ECDЯ=;:VUj:Aۚ<4(P$"9E2:S-luXuk=BWMI,!GeˆJ #%;-&A-BD*@G$g>9KI,q$McA%-cj׺KDة,p6t*C)->\~QD~Iߖ*BjbH\44Ttg R*bLc`:bghh{Iwvst`"*2]O&s.q/sIdzjJWA!i6D)1?̇|{;sJ=SUT O>BI9mpHk9sQ%`4,SXL{w0)'Rݩkp%zN(e]Bo CәagL K-̌9 (v6ӖB Za ԍ&S$.&+Jkr%l u;fBխH%NB @,IsXcLiq! #)IցxVlEm|)P3>z8U}[n4VcgJ;k>o|01"v%I"RM&X=eZEڵ'wZ ]%2í[X:G,H5h6[2̬RQy%1FLe0Ң;AUK̬&Hw\O Bܹ8_rG4) ܓBs.s~MB{~[TfYVoK/moOOg3*Z2p52KR.FM 5>."l,KN{=]4uu (JyťZJ%D # j.]D}*uI$6( 9\w 3hP5U%Zx/Rg,2`JDӦ'DGBQejZ F )|+ʈH##RG"A)xYF)nPU 'ODBhd)ҵѶ "iDww%W9\5<SjAirBgn4F15[n߽)$RULsΜլK 8E[(PM t2.jRqNѶH4BYJbZ!'tRQ Z'eCJudUuYD3;8WH: Ңfbo{CwNa~/-uB+Eɰ#ZĞ&u%ՙ u0 BĤ2L]LEP+%Ba +(TM;w$1D}Gׯ?J{/̐g4ĭW GQI*[X޹C;G%6XevK>ɬJ͇uBRIT"AX=I'LX82 cgړ$8O$HRb*_Α! ;&sT;0Rhmײn,W%jS(EekF35ʷ'tcq~گ[˶'yyDmqŅ\֜x'\QXìZ $0 XPB IDATx,4Gj R&//uCeHJko1?k!Pi+*J$Ȍ;Cz}әN~Ҷ%*JH$ %_UGҶ%PrU ztTįjQ)$JUGBF f-Qep=}_FVҠISsLUpU*,*FJg78X9{ R T{G|ak$7#Hi ) 9gZ HaQ&!%2o B fVOa:CJ6(2!'b%jq6,&wDY8:yO?=lӂ64C{.{_DP5RHB.z<ݸN+ZUh!&D[.ܣQk|6-I,'%?hJ*tݗLKqa }j-MSs!8R`Rϱ$ih.a늺T&{6.|czDB Ψ^D.>yH@FJRSJ F][1 whkֲvEYU5j*L]'t5 c D"kR cgbw+DfjL0%>>TLh8RUy DC߿6iyuml+bi{RJgS5}"UE%bFq,~JbeCRH4!"K :B MJjZd/(_dpP=}: rT,U%1Jb!p8!)Rh[7d"tw| _[1}hlض@e IP#U}e+BSO]P8, ,f KyFr0Ӛ{/r*-uZL&,WkrL,sӪ Ya뚜BJLe:r`0RS!nghU iՔ,FѣMJ(=,;__ DvE4F"ݻu>gșs&"DYb1`P:%~BXnI_yhMdbRo;]?)1c1Q̴ftCzXPJ0PTΑպcrf\(}UE:6UU6qkOUvb'ХRiNw$G!dFmu HIp%tE4?ۏ]l>CiC&ZPBБd^H2-B$Z _ٻoT2 1iBng'$m,I2u8ZAu,%DU7LUO%W_4;B<E K z*i,9O1BEyӶ)K>K49dUAHRjH"#IifsOX7H1A7zql۪^\|YBz$$nhc"aL FkI9ѶεtM*=6JxDr])^7.Wt*A23m&L"X,nt>@TL{5H[a$}"јkwo~_Fxěm{>'^O> #=un\WxKfuc R" I9zO #h%W%S}O0.0JǢU;(]XKmݽVR5'OHD[lֆ, 'ENmA6 DcPe8)ʲ\.ך5%Pp}և+ c{d1ch k ]$LMC%P TE%zVGuNӂs5bqE̱RB6(4Zr@/:#Q6|So8"mI}Bʕ;‹ٱsvIK;2M=jS$UO^u>9O>Ş =yY` #5Y*bxWi+~H=m !$>dlMX ~̲Zٮ6s:b'Rd` dΡZe$Gj6Ɲ~_ 6lr ٰ1kkgW~~s HO]b=yw>+W/\{Wg4)e"mkmדsaejZH@z*Uϴw1'/r҆;KQGZK]YRA(fz1HK:,ߓ`_ڞjIL#4VA6׿Qc:Y0i (J"SI]Fh Y!bƅ'xJJX)QйaSa[Zq̙x32J,bѡlEj>ER 3Fit]jaQJ1jΰ_S cY9:>7ê#ݟٛ-~h㓪z37Ց/Jy/(#CGb/jU`2( %Ģpt!1H-!ZR>6_BFCz4uE 3FˣyOv@IdZ 8h 3dQ/}~W^{p?>`Mİs7F??}wz=/gb#Ĉ0]Y`M$PdQ!95$ RRbU)' B )02Q)UVhPһC|,MeK̚;HS$iEm,$5] 9'*!1SW r?PmȔQU6w?~~'Dc :7XÖ5iCjݯ=_C|]Q8.F#* 12i* @ k;V'4m LG,`/ܺy=ԓrϝ=͛J*H)$_l>G+uH HRȵ[5X*J2#-b*UrRJkdqG.1Fb% {R4-<iV5) qȢS IBzeC_\ETtJ >AJ qC.RO *Hrd=D3PM$ѪLK9+)('>yPWmlck%`06ͺh>s>gC?>e;Q=jY.[ڶTjPqq{O||Qҹǟ<{fJmka\E%+d4!ŔE#%k]K(#]"y2tQJNA- !g<{{ ]O߯hX\IF(2]s]Ѭ[('#<}څ2S1W+\pXmxeKΙlղ'-݊e-R(TE}N!:ncuDô`jCJ <}r"?&m~?؎ } CP>zn!mpգVNxӛW.-n\xKk/7fe 3GqjRGIED0& pеT 9/|ۺ%(YDQ)E7--rHYdi-18*3V8(3p!bf:‡C;P%|LI)ЯtG)lZrBA()g-o֡o' 9tbuѲ͜p)>|G>g?y'kܼ HdZY.CUzN\YV#Sȉl::p]0Z&9}\:w*BJ=j+eDm] Ͳf:9nx3l oyD~s~s/gn^zWOݸ:~ʤJV${{s{m=ਆH6SlZ(Q'SĄ'G5W)[bPȮ#7^F î,VƭKT K-FeK.z* t-\L"NH%QJac)sSBc18qct۳<1>%w{S_|O}4`睗뗯?yµ'{)Rz9o#([=#礦"uLhI(ς3)U5pdJҷ~7~7leoE]`0TmVG4&!oqGC̓l޳y*$sfwo7O'q~w>sMTQ4UM=Y*TBPH&%Lz |I$(Udٌ$eJ"עeB3RtV,JtnJer/,y\1.1,ڮԯІO+J@lj,Nǭ`ề]/"R g+/ _/c&?^xO_=ʹIe|' ]UL aAj01QOfd!P5POCwoWWC۴Cm p?Ğ~# C~"x3 V6/>󥻿uz;{z;^矾pY؊}qlb1XCCil:ÅRUᤔ9Ke o{h1V 属g1lflf67mض/Ib 8yqcs^3gyb~ 3Mc$ VL&-!e/Hy|NJJ M*$"9 ,x*S@&!kEbRSTUқ+'eE|8',Zq~~Ǚ !X95Cz|Ǚ1y֍˗icڶ9 AHyTF"bdն!r1 N&M[_LzY|w+5JCIBST*zc)([M+le kG?G"c8 .#Y8F xX1)r.UtkRz;~}~c^ׅN!$0Jz2 {WnoxP^F߸K?ve"+aþ`k67(f@߬0;~Sqk&xbS7Oݸ23{0**-#E1d'@% T{RWdQ|R$"ROK$[b̐ő|qmLĂ]+z=k0l)eG7cv ܍w?ŻeV IDATl>&Xx};^8\G 1?mv+Iox}q~u37-n='_xq?ot6%I$\r#ͷ6PQ5}m7?}}q/MmΙ2Z <=B &f/~k0Ve!7 4#<|3bcye(3w̢A`4q31ga;l][cwޱp[m!A 4NlxY1?l룀$8wۮyspħ>ƓW&/ܺ9{g7n\_=vfE9mCp|n`xMNfmp oCw3I6) f? 7/8w=0uݘ(}|qs{r~/3X^X./n^LM)o8]ilZަP m:nnn M擾K?V(qle̥AQluprFڵ߿ HO 'pƛmǧc`pͲs5Ϯ 㱶!e6`,0Bikcvgκ v?mrC6y\hpUiUGq#lyqDH6mO ;fks'i4" 6||6l!1mm.KI\q| ;ppnlm3l=6ː76߸iͷ!0ALmDiiw  cZ8Ϊm?&7t~h ox{!8Z86Pf1'1naϰf?"Mo;N&ݳbox=p=8+7۱%` @Y63m l1jÃHK&ma`Ƅ7C2|3\[3S8Qxp7wig8=0I-|j?N;`i,wX8Ify6lig}*ѿ Dzq֦_[E=f9=}3ě Ȝe~l struct interval { int first; int last; }; /* auxiliary function for binary search in interval table */ static int bisearch(wchar_t ucs, const struct interval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ int emu_wcwidth(uni_t ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } /* Unused by wordgrinder --- dg */ #if 0 int mk_wcswidth(const wchar_t *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth(*pwcs)) < 0) return -1; else width += w; return width; } /* * The following functions are the same as mk_wcwidth() and * mk_wcswidth(), except that spacing characters in the East Asian * Ambiguous (A) category as defined in Unicode Technical Report #11 * have a column width of 2. This variant might be useful for users of * CJK legacy encodings who want to migrate to UCS without changing * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ int mk_wcwidth_cjk(wchar_t ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ static const struct interval ambiguous[] = { { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } }; /* binary search in table of non-spacing characters */ if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1)) return 2; return mk_wcwidth(ucs); } int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth_cjk(*pwcs)) < 0) return -1; else width += w; return width; } #endif wordgrinder-0.5.1.orig/src/c/globals.h0000644000000000000000000000471012243254677014513 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #ifndef GLOBALS_H #define GLOBALS_H #include #include #include #include #include #include #include /* --- Emulation issues -------------------------------------------------- */ typedef int uni_t; #if defined EMULATED_WCWIDTH extern int emu_wcwidth(uni_t c); #else #include #define emu_wcwidth(c) wcwidth(c) #endif extern int main(int argc, const char* argv[]); /* --- Lua --------------------------------------------------------------- */ #include #include #include extern lua_State* L; typedef struct { const char* data; size_t size; const char* name; } FileDescriptor; extern const FileDescriptor script_table[]; extern void script_init(void); extern void script_load(const char* filename); extern void script_load_from_table(const FileDescriptor* table); extern void script_run(const char* argv[]); /* --- Screen management ------------------------------------------------- */ extern void screen_init(const char* argv[]); extern void screen_deinit(void); /* --- Word management --------------------------------------------------- */ extern void word_init(void); /* --- Zipfile management ------------------------------------------------ */ extern void zip_init(void); /* --- General utilities ------------------------------------------------- */ extern int getu8bytes(char c); extern uni_t readu8(const char** ptr); extern void writeu8(char** ptr, uni_t value); extern void utils_init(void); /* --- Display layer ----------------------------------------------------- */ enum { /* These four are also style control codes. */ DPY_ITALIC = (1<<0), DPY_UNDERLINE = (1<<1), DPY_REVERSE = (1<<2), DPY_BOLD = (1<<3), /* These cannot appear in text. */ DPY_BRIGHT = (1<<4), DPY_DIM = (1<<5), }; extern void dpy_init(const char* argv[]); extern void dpy_start(void); extern void dpy_shutdown(void); extern void dpy_setattr(int andmask, int ormask); extern void dpy_writechar(int x, int y, uni_t c); extern void dpy_setcursor(int x, int y); extern void dpy_clearscreen(void); extern void dpy_sync(void); extern void dpy_cleararea(int x1, int y1, int x2, int y2); extern void dpy_getscreensize(int* x, int* y); extern uni_t dpy_getchar(int timeout); extern const char* dpy_getkeyname(uni_t key); #endif wordgrinder-0.5.1.orig/src/c/lfs/0000755000000000000000000000000012251160511013457 5ustar wordgrinder-0.5.1.orig/src/c/lfs/COPYING0000644000000000000000000000204112123643306014515 0ustar Copyright © 2003 Kepler Project. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.wordgrinder-0.5.1.orig/src/c/lfs/lfs.c0000644000000000000000000006033212243251035014416 0ustar /* ** LuaFileSystem ** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) ** ** File system manipulation library. ** This library offers these functions: ** lfs.attributes (filepath [, attributename]) ** lfs.chdir (path) ** lfs.currentdir () ** lfs.dir (path) ** lfs.lock (fh, mode) ** lfs.lock_dir (path) ** lfs.mkdir (path) ** lfs.rmdir (path) ** lfs.setmode (filepath, mode) ** lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts ** lfs.touch (filepath [, atime [, mtime]]) ** lfs.unlock (fh) ** ** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $ */ #ifndef _WIN32 #ifndef _AIX #define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ #else #define _LARGE_FILES 1 /* AIX */ #endif #endif #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #ifdef _WIN32 #include #include #include #include #ifdef __BORLANDC__ #include #else #include #endif #include #else #include #include #include #include #include #endif #include #include #include #include "lfs.h" #define LFS_VERSION "1.6.2" #define LFS_LIBNAME "lfs" #if LUA_VERSION_NUM < 502 # define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l)) #endif /* Define 'strerror' for systems that do not implement it */ #ifdef NO_STRERROR #define strerror(_) "System unable to describe the error" #endif /* Define 'getcwd' for systems that do not implement it */ #ifdef NO_GETCWD #define getcwd(p,s) NULL #define getcwd_error "Function 'getcwd' not provided by system" #else #define getcwd_error strerror(errno) #ifdef _WIN32 /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ #define LFS_MAXPATHLEN MAX_PATH #else /* For MAXPATHLEN: */ #include #define LFS_MAXPATHLEN MAXPATHLEN #endif #endif #define DIR_METATABLE "directory metatable" typedef struct dir_data { int closed; #ifdef _WIN32 long hFile; char pattern[MAX_PATH+1]; #else DIR *dir; #endif } dir_data; #define LOCK_METATABLE "lock metatable" #ifdef _WIN32 #ifdef __BORLANDC__ #define lfs_setmode(L,file,m) ((void)L, setmode(_fileno(file), m)) #define STAT_STRUCT struct stati64 #else #define lfs_setmode(L,file,m) ((void)L, _setmode(_fileno(file), m)) #define STAT_STRUCT struct _stati64 #endif #define STAT_FUNC _stati64 #define LSTAT_FUNC STAT_FUNC #else #define _O_TEXT 0 #define _O_BINARY 0 #define lfs_setmode(L,file,m) ((void)L, (void)file, (void)m, 0) #define STAT_STRUCT struct stat #define STAT_FUNC stat #define LSTAT_FUNC lstat #endif /* ** Utility functions */ static int pusherror(lua_State *L, const char *info) { lua_pushnil(L); if (info==NULL) lua_pushstring(L, strerror(errno)); else lua_pushfstring(L, "%s: %s", info, strerror(errno)); lua_pushinteger(L, errno); return 3; } static int pushresult(lua_State *L, int i, const char *info) { if (i==-1) return pusherror(L, info); lua_pushinteger(L, i); return 1; } /* ** This function changes the working (current) directory */ static int change_dir (lua_State *L) { const char *path = luaL_checkstring(L, 1); if (chdir(path)) { lua_pushnil (L); lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", path, chdir_error); return 2; } else { lua_pushboolean (L, 1); return 1; } } /* ** This function returns the current directory ** If unable to get the current directory, it returns nil ** and a string describing the error */ static int get_dir (lua_State *L) { char *path; /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ char buf[LFS_MAXPATHLEN]; if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) { lua_pushnil(L); lua_pushstring(L, getcwd_error); return 2; } else { lua_pushstring(L, path); return 1; } } /* ** Check if the given element on the stack is a file and returns it. */ static FILE *check_file (lua_State *L, int idx, const char *funcname) { FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); if (fh == NULL) { luaL_error (L, "%s: not a file", funcname); return 0; } else if (*fh == NULL) { luaL_error (L, "%s: closed file", funcname); return 0; } else return *fh; } /* ** */ static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { int code; #ifdef _WIN32 /* lkmode valid values are: LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. LK_NBRLCK Same as _LK_NBLCK. LK_RLCK Same as _LK_LOCK. LK_UNLCK Unlocks the specified bytes, which must have been previously locked. Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp */ int lkmode; switch (*mode) { case 'r': lkmode = LK_NBLCK; break; case 'w': lkmode = LK_NBLCK; break; case 'u': lkmode = LK_UNLCK; break; default : return luaL_error (L, "%s: invalid mode", funcname); } if (!len) { fseek (fh, 0L, SEEK_END); len = ftell (fh); } fseek (fh, start, SEEK_SET); #ifdef __BORLANDC__ code = locking (fileno(fh), lkmode, len); #else code = _locking (fileno(fh), lkmode, len); #endif #else struct flock f; switch (*mode) { case 'w': f.l_type = F_WRLCK; break; case 'r': f.l_type = F_RDLCK; break; case 'u': f.l_type = F_UNLCK; break; default : return luaL_error (L, "%s: invalid mode", funcname); } f.l_whence = SEEK_SET; f.l_start = (off_t)start; f.l_len = (off_t)len; code = fcntl (fileno(fh), F_SETLK, &f); #endif return (code != -1); } #ifdef _WIN32 typedef struct lfs_Lock { HANDLE fd; } lfs_Lock; static int lfs_lock_dir(lua_State *L) { size_t pathl; HANDLE fd; lfs_Lock *lock; char *ln; const char *lockfile = "/lockfile.lfs"; const char *path = luaL_checklstring(L, 1, &pathl); ln = (char*)malloc(pathl + strlen(lockfile) + 1); if(!ln) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } strcpy(ln, path); strcat(ln, lockfile); if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { int en = GetLastError(); free(ln); lua_pushnil(L); if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) lua_pushstring(L, "File exists"); else lua_pushstring(L, strerror(en)); return 2; } free(ln); lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); lock->fd = fd; luaL_getmetatable (L, LOCK_METATABLE); lua_setmetatable (L, -2); return 1; } static int lfs_unlock_dir(lua_State *L) { lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); CloseHandle(lock->fd); return 0; } #else typedef struct lfs_Lock { char *ln; } lfs_Lock; static int lfs_lock_dir(lua_State *L) { lfs_Lock *lock; size_t pathl; char *ln; const char *lockfile = "/lockfile.lfs"; const char *path = luaL_checklstring(L, 1, &pathl); lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); ln = (char*)malloc(pathl + strlen(lockfile) + 1); if(!ln) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } strcpy(ln, path); strcat(ln, lockfile); if(symlink("lock", ln) == -1) { free(ln); lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } lock->ln = ln; luaL_getmetatable (L, LOCK_METATABLE); lua_setmetatable (L, -2); return 1; } static int lfs_unlock_dir(lua_State *L) { lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); if(lock->ln) { unlink(lock->ln); free(lock->ln); lock->ln = NULL; } return 0; } #endif static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { static const int mode[] = {_O_BINARY, _O_TEXT}; static const char *const modenames[] = {"binary", "text", NULL}; int op = luaL_checkoption(L, arg, NULL, modenames); int res = lfs_setmode(L, f, mode[op]); if (res != -1) { int i; lua_pushboolean(L, 1); for (i = 0; modenames[i] != NULL; i++) { if (mode[i] == res) { lua_pushstring(L, modenames[i]); goto exit; } } lua_pushnil(L); exit: return 2; } else { int en = errno; lua_pushnil(L); lua_pushfstring(L, "%s", strerror(en)); lua_pushinteger(L, en); return 3; } } static int lfs_f_setmode(lua_State *L) { return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); } /* ** Locks a file. ** @param #1 File handle. ** @param #2 String with lock mode ('w'rite, 'r'ead). ** @param #3 Number with start position (optional). ** @param #4 Number with length (optional). */ static int file_lock (lua_State *L) { FILE *fh = check_file (L, 1, "lock"); const char *mode = luaL_checkstring (L, 2); const long start = luaL_optlong (L, 3, 0); long len = luaL_optlong (L, 4, 0); if (_file_lock (L, fh, mode, start, len, "lock")) { lua_pushboolean (L, 1); return 1; } else { lua_pushnil (L); lua_pushfstring (L, "%s", strerror(errno)); return 2; } } /* ** Unlocks a file. ** @param #1 File handle. ** @param #2 Number with start position (optional). ** @param #3 Number with length (optional). */ static int file_unlock (lua_State *L) { FILE *fh = check_file (L, 1, "unlock"); const long start = luaL_optlong (L, 2, 0); long len = luaL_optlong (L, 3, 0); if (_file_lock (L, fh, "u", start, len, "unlock")) { lua_pushboolean (L, 1); return 1; } else { lua_pushnil (L); lua_pushfstring (L, "%s", strerror(errno)); return 2; } } /* ** Creates a link. ** @param #1 Object to link to. ** @param #2 Name of link. ** @param #3 True if link is symbolic (optional). */ static int make_link(lua_State *L) { #ifndef _WIN32 const char *oldpath = luaL_checkstring(L, 1); const char *newpath = luaL_checkstring(L, 2); return pushresult(L, (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); #else return pusherror(L, "make_link is not supported on Windows"); #endif } /* ** Creates a directory. ** @param #1 Directory path. */ static int make_dir (lua_State *L) { const char *path = luaL_checkstring (L, 1); int fail; #ifdef _WIN32 fail = _mkdir (path); #else fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); #endif if (fail) { lua_pushnil (L); lua_pushfstring (L, "%s", strerror(errno)); return 2; } lua_pushboolean (L, 1); return 1; } /* ** Removes a directory. ** @param #1 Directory path. */ static int remove_dir (lua_State *L) { const char *path = luaL_checkstring (L, 1); int fail; fail = rmdir (path); if (fail) { lua_pushnil (L); lua_pushfstring (L, "%s", strerror(errno)); return 2; } lua_pushboolean (L, 1); return 1; } /* ** Directory iterator */ static int dir_iter (lua_State *L) { #ifdef _WIN32 struct _finddata_t c_file; #else struct dirent *entry; #endif dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); luaL_argcheck (L, d->closed == 0, 1, "closed directory"); #ifdef _WIN32 if (d->hFile == 0L) { /* first entry */ if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { lua_pushnil (L); lua_pushstring (L, strerror (errno)); d->closed = 1; return 2; } else { lua_pushstring (L, c_file.name); return 1; } } else { /* next entry */ if (_findnext (d->hFile, &c_file) == -1L) { /* no more entries => close directory */ _findclose (d->hFile); d->closed = 1; return 0; } else { lua_pushstring (L, c_file.name); return 1; } } #else if ((entry = readdir (d->dir)) != NULL) { lua_pushstring (L, entry->d_name); return 1; } else { /* no more entries => close directory */ closedir (d->dir); d->closed = 1; return 0; } #endif } /* ** Closes directory iterators */ static int dir_close (lua_State *L) { dir_data *d = (dir_data *)lua_touserdata (L, 1); #ifdef _WIN32 if (!d->closed && d->hFile) { _findclose (d->hFile); } #else if (!d->closed && d->dir) { closedir (d->dir); } #endif d->closed = 1; return 0; } /* ** Factory of directory iterators */ static int dir_iter_factory (lua_State *L) { const char *path = luaL_checkstring (L, 1); dir_data *d; lua_pushcfunction (L, dir_iter); d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); luaL_getmetatable (L, DIR_METATABLE); lua_setmetatable (L, -2); d->closed = 0; #ifdef _WIN32 d->hFile = 0L; if (strlen(path) > MAX_PATH-2) luaL_error (L, "path too long: %s", path); else sprintf (d->pattern, "%s/*", path); #else d->dir = opendir (path); if (d->dir == NULL) luaL_error (L, "cannot open %s: %s", path, strerror (errno)); #endif return 2; } /* ** Creates directory metatable. */ static int dir_create_meta (lua_State *L) { luaL_newmetatable (L, DIR_METATABLE); /* Method table */ lua_newtable(L); lua_pushcfunction (L, dir_iter); lua_setfield(L, -2, "next"); lua_pushcfunction (L, dir_close); lua_setfield(L, -2, "close"); /* Metamethods */ lua_setfield(L, -2, "__index"); lua_pushcfunction (L, dir_close); lua_setfield (L, -2, "__gc"); return 1; } /* ** Creates lock metatable. */ static int lock_create_meta (lua_State *L) { luaL_newmetatable (L, LOCK_METATABLE); /* Method table */ lua_newtable(L); lua_pushcfunction(L, lfs_unlock_dir); lua_setfield(L, -2, "free"); /* Metamethods */ lua_setfield(L, -2, "__index"); lua_pushcfunction(L, lfs_unlock_dir); lua_setfield(L, -2, "__gc"); return 1; } #ifdef _WIN32 #ifndef S_ISDIR #define S_ISDIR(mode) (mode&_S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(mode) (mode&_S_IFREG) #endif #ifndef S_ISLNK #define S_ISLNK(mode) (0) #endif #ifndef S_ISSOCK #define S_ISSOCK(mode) (0) #endif #ifndef S_ISFIFO #define S_ISFIFO(mode) (0) #endif #ifndef S_ISCHR #define S_ISCHR(mode) (mode&_S_IFCHR) #endif #ifndef S_ISBLK #define S_ISBLK(mode) (0) #endif #endif /* ** Convert the inode protection mode to a string. */ #ifdef _WIN32 static const char *mode2string (unsigned short mode) { #else static const char *mode2string (mode_t mode) { #endif if ( S_ISREG(mode) ) return "file"; else if ( S_ISDIR(mode) ) return "directory"; else if ( S_ISLNK(mode) ) return "link"; else if ( S_ISSOCK(mode) ) return "socket"; else if ( S_ISFIFO(mode) ) return "named pipe"; else if ( S_ISCHR(mode) ) return "char device"; else if ( S_ISBLK(mode) ) return "block device"; else return "other"; } /* ** Set access time and modification values for file */ static int file_utime (lua_State *L) { const char *file = luaL_checkstring (L, 1); struct utimbuf utb, *buf; if (lua_gettop (L) == 1) /* set to current date/time */ buf = NULL; else { utb.actime = (time_t)luaL_optnumber (L, 2, 0); utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime); buf = &utb; } if (utime (file, buf)) { lua_pushnil (L); lua_pushfstring (L, "%s", strerror (errno)); return 2; } lua_pushboolean (L, 1); return 1; } /* inode protection mode */ static void push_st_mode (lua_State *L, STAT_STRUCT *info) { lua_pushstring (L, mode2string (info->st_mode)); } /* device inode resides on */ static void push_st_dev (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_dev); } /* inode's number */ static void push_st_ino (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_ino); } /* number of hard links to the file */ static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_nlink); } /* user-id of owner */ static void push_st_uid (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_uid); } /* group-id of owner */ static void push_st_gid (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_gid); } /* device type, for special file inode */ static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_rdev); } /* time of last access */ static void push_st_atime (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, info->st_atime); } /* time of last data modification */ static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, info->st_mtime); } /* time of last file status change */ static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, info->st_ctime); } /* file size, in bytes */ static void push_st_size (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_size); } #ifndef _WIN32 /* blocks allocated for file */ static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_blocks); } /* optimal file system I/O blocksize */ static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_blksize); } #endif static void push_invalid (lua_State *L, STAT_STRUCT *info) { luaL_error(L, "invalid attribute name"); #ifndef _WIN32 info->st_blksize = 0; /* never reached */ #endif } /* ** Convert the inode protection mode to a permission list. */ #ifdef _WIN32 static const char *perm2string (unsigned short mode) { static char perms[10] = "---------\0"; int i; for (i=0;i<9;i++) perms[i]='-'; if (mode & _S_IREAD) { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } if (mode & _S_IWRITE) { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } if (mode & _S_IEXEC) { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } return perms; } #else static const char *perm2string (mode_t mode) { static char perms[10] = "---------\0"; int i; for (i=0;i<9;i++) perms[i]='-'; if (mode & S_IRUSR) perms[0] = 'r'; if (mode & S_IWUSR) perms[1] = 'w'; if (mode & S_IXUSR) perms[2] = 'x'; if (mode & S_IRGRP) perms[3] = 'r'; if (mode & S_IWGRP) perms[4] = 'w'; if (mode & S_IXGRP) perms[5] = 'x'; if (mode & S_IROTH) perms[6] = 'r'; if (mode & S_IWOTH) perms[7] = 'w'; if (mode & S_IXOTH) perms[8] = 'x'; return perms; } #endif /* permssions string */ static void push_st_perm (lua_State *L, STAT_STRUCT *info) { lua_pushstring (L, perm2string (info->st_mode)); } typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); struct _stat_members { const char *name; _push_function push; }; struct _stat_members members[] = { { "mode", push_st_mode }, { "dev", push_st_dev }, { "ino", push_st_ino }, { "nlink", push_st_nlink }, { "uid", push_st_uid }, { "gid", push_st_gid }, { "rdev", push_st_rdev }, { "access", push_st_atime }, { "modification", push_st_mtime }, { "change", push_st_ctime }, { "size", push_st_size }, { "permissions", push_st_perm }, #ifndef _WIN32 { "blocks", push_st_blocks }, { "blksize", push_st_blksize }, #endif { NULL, push_invalid } }; /* ** Get file or symbolic link information */ static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { int i; STAT_STRUCT info; const char *file = luaL_checkstring (L, 1); if (st(file, &info)) { lua_pushnil (L); lua_pushfstring (L, "cannot obtain information from file `%s'", file); return 2; } if (lua_isstring (L, 2)) { int v; const char *member = lua_tostring (L, 2); if (strcmp (member, "mode") == 0) v = 0; #ifndef _WIN32 else if (strcmp (member, "blocks") == 0) v = 11; else if (strcmp (member, "blksize") == 0) v = 12; #endif else /* look for member */ for (v = 1; members[v].name; v++) if (*members[v].name == *member) break; /* push member value and return */ members[v].push (L, &info); return 1; } else if (!lua_istable (L, 2)) /* creates a table if none is given */ lua_newtable (L); /* stores all members in table on top of the stack */ for (i = 0; members[i].name; i++) { lua_pushstring (L, members[i].name); members[i].push (L, &info); lua_rawset (L, -3); } return 1; } /* ** Get file information using stat. */ static int file_info (lua_State *L) { return _file_info_ (L, STAT_FUNC); } /* ** Get symbolic link information using lstat. */ static int link_info (lua_State *L) { return _file_info_ (L, LSTAT_FUNC); } /* ** Assumes the table is on top of the stack. */ static void set_info (lua_State *L) { lua_pushliteral (L, "_COPYRIGHT"); lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); lua_settable (L, -3); lua_pushliteral (L, "_DESCRIPTION"); lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); lua_settable (L, -3); lua_pushliteral (L, "_VERSION"); lua_pushliteral (L, "LuaFileSystem "LFS_VERSION); lua_settable (L, -3); } static const struct luaL_Reg fslib[] = { {"attributes", file_info}, {"chdir", change_dir}, {"currentdir", get_dir}, {"dir", dir_iter_factory}, {"link", make_link}, {"lock", file_lock}, {"mkdir", make_dir}, {"rmdir", remove_dir}, {"symlinkattributes", link_info}, {"setmode", lfs_f_setmode}, {"touch", file_utime}, {"unlock", file_unlock}, {"lock_dir", lfs_lock_dir}, {NULL, NULL}, }; int luaopen_lfs (lua_State *L) { dir_create_meta (L); lock_create_meta (L); luaL_newlib (L, fslib); lua_pushvalue(L, -1); lua_setglobal(L, LFS_LIBNAME); set_info (L); return 1; } wordgrinder-0.5.1.orig/src/c/lfs/lfs.h0000644000000000000000000000062412121172740014421 0ustar /* ** LuaFileSystem ** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) ** ** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $ */ /* Define 'chdir' for systems that do not implement it */ #ifdef NO_CHDIR #define chdir(p) (-1) #define chdir_error "Function 'chdir' not provided by system" #else #define chdir_error strerror(errno) #endif int luaopen_lfs (lua_State *L); wordgrinder-0.5.1.orig/src/c/lua.c0000644000000000000000000000477512243251035013640 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include "globals.h" #if defined BUILTIN_LFS #include "lfs/lfs.h" #endif lua_State* L; static int report(lua_State* L, int status) { if (status && !lua_isnil(L, -1)) { const char* msg = lua_tostring(L, -1); if (!msg) msg = "(error object is not a string)"; screen_deinit(); fprintf(stderr, "Lua error: %s\n", msg); lua_pop(L, 1); exit(1); } return status; } static int traceback (lua_State *L) { lua_pushglobaltable(L); lua_getfield(L, -1, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return 1; } lua_getfield(L, -1, "traceback"); if (!lua_isfunction(L, -1)) { lua_pop(L, 2); return 1; } lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ return 1; } static int docall(lua_State* L, int narg, int clear) { int base = lua_gettop(L) - narg; lua_pushcfunction(L, traceback); lua_insert(L, base); int status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); lua_remove(L, base); if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); return status; } void script_deinit(void) { lua_close(L); } void script_init(void) { L = luaL_newstate(); luaL_openlibs(L); #if defined BUILTIN_LFS luaopen_lfs(L); #endif atexit(script_deinit); /* Set some global variables. */ lua_pushstring(L, VERSION); lua_setglobal(L, "VERSION"); lua_pushnumber(L, FILEFORMAT); lua_setglobal(L, "FILEFORMAT"); lua_pushstring(L, ARCH); lua_setglobal(L, "ARCH"); lua_newtable(L); lua_setglobal(L, "wg"); lua_pushboolean(L, #ifndef NDEBUG 1 #else 0 #endif ); lua_setglobal(L, "DEBUG"); } void script_load(const char* filename) { int status = luaL_loadfile(L, filename); status = status || docall(L, 0, 1); (void) report(L, status); } void script_load_from_table(const FileDescriptor* table) { while (table->data) { int status = luaL_loadbuffer(L, table->data, table->size, table->name); status = status || docall(L, 0, 1); if (status) { (void) report(L, status); break; } table++; } } void script_run(const char* argv[]) { lua_getglobal(L, "Main"); /* Push the arguments onto the stack. */ int argc = 0; for (;;) { const char* s = *argv++; if (!s) break; lua_pushstring(L, s); argc++; } /* Call the main program. */ int status = docall(L, argc, 1); (void) report(L, status); } wordgrinder-0.5.1.orig/src/c/main.c0000644000000000000000000000074112121432565013774 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include #include #include #include "globals.h" int main(int argc, const char* argv[]) { setlocale(LC_ALL, ""); setlocale(LC_COLLATE, "C"); script_init(); screen_init(argv); word_init(); utils_init(); zip_init(); script_load_from_table(script_table); script_run(argv); return 0; } wordgrinder-0.5.1.orig/src/c/minizip/0000755000000000000000000000000012251160511014352 5ustar wordgrinder-0.5.1.orig/src/c/minizip/COPYING0000644000000000000000000000255612123642415015423 0ustar MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson Credits Gilles Vollant - Original MiniZip author Even Rouault - ZIP64 unzip Support Daniel Borca - BZip Compression method support in unzip Mathias Svensson - ZIP64 zip support Mathias Svensson - BZip Compression method support in zip License ---------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ----------------------------------------------------------wordgrinder-0.5.1.orig/src/c/minizip/README0000644000000000000000000000014412121126227015233 0ustar This is a stripped-down version of minizip 1.1. See the COPYING file for the redistribution terms. wordgrinder-0.5.1.orig/src/c/minizip/crypt.h0000644000000000000000000001117712132346214015677 0ustar /* crypt.h -- base code for crypt/uncrypt ZIPfile Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). If you don't need crypting in your application, just define symbols NOCRYPT and NOUNCRYPT. This code support the "Traditional PKWARE Encryption". The new AES encryption added on Zip format by Winzip (see the page http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong Encryption is not supported. */ #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) /*********************************************************************** * Return the next byte in the pseudo-random sequence */ static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } /*********************************************************************** * Update the encryption keys with the next byte of plain text */ static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; { register int keyshift = (int)((*(pkeys+1)) >> 24); (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); } return c; } /*********************************************************************** * Initialize the encryption keys and the random header according to * the given password. */ static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; *(pkeys+2) = 878082192L; while (*passwd != '\0') { update_keys(pkeys,pcrc_32_tab,(int)*passwd); passwd++; } } #define zdecode(pkeys,pcrc_32_tab,c) \ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif static int crypthead(const char* passwd, /* password string */ unsigned char* buf, /* where to write header */ int bufSize, unsigned long* pkeys, const z_crc_t* pcrc_32_tab, unsigned long crcForCrypting) { int n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN-2]; /* random header */ static unsigned calls = 0; /* ensure different random header each time */ if (bufSize> 7) & 0xff; header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); } /* Encrypt random header (last two bytes is high word of crc) */ init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN-2; n++) { buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); } buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); return n; } #endif wordgrinder-0.5.1.orig/src/c/minizip/ioapi.c0000644000000000000000000002004112121126227015616 0ustar /* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt */ #if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) #define _CRT_SECURE_NO_WARNINGS #endif #if defined(__APPLE__) || defined(IOAPI_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) #else #define FOPEN_FUNC(filename, mode) fopen64(filename, mode) #define FTELLO_FUNC(stream) ftello64(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) #endif #include "ioapi.h" voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); else { return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); } } long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else { uLong offsetTruncated = (uLong)offset; if (offsetTruncated != offset) return -1; else return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); } } ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == MAXU32) return (ZPOS64_T)-1; else return tell_uLong; } } void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; p_filefunc64_32->zfile_func64.zseek64_file = NULL; p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; } static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = fopen(filename, mode_fopen); return file; } static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = FOPEN_FUNC((const char*)filename, mode_fopen); return file; } static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) { uLong ret; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) { long ret; ret = ftell((FILE *)stream); return ret; } static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) { ZPOS64_T ret; ret = FTELLO_FUNC((FILE *)stream); return ret; } static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; if (fseek((FILE *)stream, offset, fseek_origin) != 0) ret = -1; return ret; } static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) ret = -1; return ret; } static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) { int ret; ret = fclose((FILE *)stream); return ret; } static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) { int ret; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc (pzlib_filefunc_def) zlib_filefunc_def* pzlib_filefunc_def; { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell_file = ftell_file_func; pzlib_filefunc_def->zseek_file = fseek_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = fopen64_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell64_file = ftell64_file_func; pzlib_filefunc_def->zseek64_file = fseek64_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } wordgrinder-0.5.1.orig/src/c/minizip/ioapi.h0000644000000000000000000001561312121126227015634 0ustar /* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. More if/def section may be needed to support other platforms Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. (but you should use iowin32.c for windows instead) */ #ifndef _ZLIBIOAPI64_H #define _ZLIBIOAPI64_H #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) // Linux needs this to support file operation on files larger then 4+GB // But might need better if/def to select just the platforms that needs them. #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #include #include #include "zlib.h" #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #else #ifdef __FreeBSD__ #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko #endif #ifdef _MSC_VER #define fopen64 fopen #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) #define ftello64 _ftelli64 #define fseeko64 _fseeki64 #else // old MSC #define ftello64 ftell #define fseeko64 fseek #endif #endif #endif /* #ifndef ZPOS64_T #ifdef _WIN32 #define ZPOS64_T fpos_t #else #include #define ZPOS64_T uint64_t #endif #endif */ #ifdef HAVE_MINIZIP64_CONF_H #include "mz64conf.h" #endif /* a type choosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" typedef uint64_t ZPOS64_T; #else /* Maximum unsigned 32-bit value used as placeholder for zip64 */ #define MAXU32 0xffffffff #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif #ifdef __cplusplus extern "C" { #endif #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); /* here is the "old" 32 bits structure structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); typedef struct zlib_filefunc64_def_s { open64_file_func zopen64_file; read_file_func zread_file; write_file_func zwrite_file; tell64_file_func ztell64_file; seek64_file_func zseek64_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc64_def; void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s { zlib_filefunc64_def zfile_func64; open_file_func zopen32_file; tell_file_func ztell32_file; seek_file_func zseek32_file; } zlib_filefunc64_32_def; #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) #ifdef __cplusplus } #endif #endif wordgrinder-0.5.1.orig/src/c/minizip/unzip.c0000644000000000000000000021263212132346214015675 0ustar /* unzip.c -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt ------------------------------------------------------------------------------------ Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). ------------------------------------------------------------------------------------ Changes in unzip.c 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* 2007-2008 - Even Rouault - Remove old C style function prototypes 2007-2008 - Even Rouault - Add unzip support for ZIP64 Copyright (C) 2007-2008 Even Rouault Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G should only read the compressed/uncompressed size from the Zip64 format if the size from normal header was 0xFFFFFFFF Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) Patch created by Daniel Borca Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson */ #include #include #include #ifndef NOUNCRYPT #define NOUNCRYPT #endif #include "zlib.h" #include "unzip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ } unz_file_info64_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ ZPOS64_T total_out_64; uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ uLong compression_method; /* compression method (0==store) */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip64_read_info_s; /* unz64_s contain internal information about the zipfile */ typedef struct { zlib_filefunc64_32_def z_filefunc; int is64bitOpenFunction; voidpf filestream; /* io structore of the zipfile */ unz_global_info64 gi; /* public global information */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T num_file; /* number of the current file in the zipfile*/ ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ ZPOS64_T central_pos; /* position of the beginning of the central dir*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info64 cur_file_info; /* public info about the current file in zip*/ unz_file_info64_internal cur_file_info_internal; /* private info about it*/ file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; int isZip64; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; # endif } unz64_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ local int unz64local_getByte OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unz64local_getShort OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { uLong x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unz64local_getLong OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { uLong x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<8; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<16; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unz64local_getLong64 OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (ZPOS64_T)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<8; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<16; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<24; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<32; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<40; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<48; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<56; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity) { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir64 OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); if (uPosFound == 0) return 0; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature, already checked */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; /* number of the disk with the start of the zip64 end of central directory */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 0) return 0; /* relative offset of the zip64 end of central directory record */ if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) return 0; /* total number of disks */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 1) return 0; /* Goto end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 0x06064b50) return 0; return relativeOffset; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ local unzFile unzOpenInternal (const void *path, zlib_filefunc64_32_def* pzlib_filefunc64_32_def, int is64bitOpenFunction) { unz64_s us; unz64_s *s; ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; us.z_filefunc.zseek32_file = NULL; us.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); else us.z_filefunc = *pzlib_filefunc64_32_def; us.is64bitOpenFunction = is64bitOpenFunction; us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); if (central_pos) { uLong uS; ZPOS64_T uL64; us.isZip64 = 1; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* size of zip64 end of central directory record */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) err=UNZ_ERRNO; /* version made by */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* version needed to extract */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory on this disk */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; us.gi.size_comment = 0; } else { central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==0) err=UNZ_ERRNO; us.isZip64 = 0; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.gi.number_entry = uL; /* total number of entries in the central dir */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; number_entry_CD = uL; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.offset_central_dir = uL; /* zipfile comment length */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; } if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE64(s->z_filefunc, s->filestream); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; *pglobal_info=s->gi; return UNZ_OK; } extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* to do : check if number_entry is not truncated */ pglobal_info32->number_entry = (uLong)s->gi.number_entry; pglobal_info32->size_comment = s->gi.size_comment; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) { ZPOS64_T uDate; uDate = (ZPOS64_T)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); local int unz64local_GetCurrentFileInfoInternal (unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz64_s* s; unz_file_info64 file_info; unz_file_info64_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; uLong uL; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (ZSEEK64(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.compressed_size = uL; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.uncompressed_size = uL; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; // relative offset of local header if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info_internal.offset_curfile = uL; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek -= uSizeRead; } // Read extrafield if ((err==UNZ_OK) && (extraField!=NULL)) { ZPOS64_T uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - (uLong)uSizeRead; } else lSeek += file_info.size_file_extra; if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) { uLong acc = 0; // since lSeek now points to after the extra field we need to move back lSeek -= file_info.size_file_extra; if (lSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } while(acc < file_info.size_file_extra) { uLong headerId; uLong dataSize; if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) err=UNZ_ERRNO; /* ZIP64 extra fields */ if (headerId == 0x0001) { uLong uL; if(file_info.uncompressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.compressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info_internal.offset_curfile == MAXU32) { /* Relative Header offset */ if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.disk_num_start == MAXU32) { /* Disk Start Number */ if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; } } else { if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) err=UNZ_ERRNO; } acc += 2 + 2 + dataSize; } } if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek+=file_info.size_file_comment - uSizeRead; } else lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, unz_file_info64 * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, unz_file_info * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { int err; unz_file_info64 file_info64; err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); if ((err==UNZ_OK) && (pfile_info != NULL)) { pfile_info->version = file_info64.version; pfile_info->version_needed = file_info64.version_needed; pfile_info->flag = file_info64.flag; pfile_info->compression_method = file_info64.compression_method; pfile_info->dosDate = file_info64.dosDate; pfile_info->crc = file_info64.crc; pfile_info->size_filename = file_info64.size_filename; pfile_info->size_file_extra = file_info64.size_file_extra; pfile_info->size_file_comment = file_info64.size_file_comment; pfile_info->disk_num_start = file_info64.disk_num_start; pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; pfile_info->tmu_date = file_info64.tmu_date, pfile_info->compressed_size = (uLong)file_info64.compressed_size; pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; } return err; } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile (unzFile file) { int err=UNZ_OK; unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile (unzFile file) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) { unz64_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info64 cur_file_infoSaved; unz_file_info64_internal cur_file_info_internalSaved; ZPOS64_T num_fileSaved; ZPOS64_T pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo64(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { ZPOS64_T pos_in_zip_directory; // offset in file ZPOS64_T num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) { unz64_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; int err = unzGetFilePos64(file,&file_pos64); if (err==UNZ_OK) { file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; file_pos->num_of_file = (uLong)file_pos64.num_of_file; } return err; } extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) { unz64_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; if (file_pos == NULL) return UNZ_PARAMERROR; file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; file_pos64.num_of_file = file_pos->num_of_file; return unzGoToFilePos64(file,&file_pos64); } /* // Unzip Helper Functions - should be here? /////////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, ZPOS64_T * poffset_local_extrafield, uInt * psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, int* level, int raw, const char* password) { int err=UNZ_OK; uInt iSizeVar; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->total_out_64=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) { #ifdef HAVE_BZIP2 pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; pfile_in_zip_read_info->bstream.bzfree = (free_func)0; pfile_in_zip_read_info->bstream.opaque = (voidpf)0; pfile_in_zip_read_info->bstream.state = (voidpf)0; pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; else { TRYFREE(pfile_in_zip_read_info); return err; } #else pfile_in_zip_read_info->raw=1; #endif } else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = 0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { TRYFREE(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; s->encrypted = 0; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK64(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile (unzFile file) { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; s=(unz64_s*)file; if (file==NULL) return 0; //UNZ_PARAMERROR; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return 0; //UNZ_PARAMERROR; return pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile; } /** Addition for GDAL : END */ /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;iread_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { #ifdef HAVE_BZIP2 uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; pfile_in_zip_read_info->bstream.total_in_hi32 = 0; pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; pfile_in_zip_read_info->bstream.total_out_hi32 = 0; uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; if (err==BZ_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=BZ_OK) break; #endif } // end Z_BZIP2ED else { ZPOS64_T uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; ZPOS64_T uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } extern ZPOS64_T ZEXPORT unztell64 (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return (ZPOS64_T)-1; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return (ZPOS64_T)-1; return pfile_in_zip_read_info->total_out_64; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; uInt read_now; ZPOS64_T size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile (unzFile file) { int err=UNZ_OK; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) inflateEnd(&pfile_in_zip_read_info->stream); #ifdef HAVE_BZIP2 else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); #endif pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) { unz64_s* s; uLong uReadThis ; if (file==NULL) return (int)UNZ_PARAMERROR; s=(unz64_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) { unz64_s* s; if (file==NULL) return 0; //UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern uLong ZEXPORT unzGetOffset (unzFile file) { ZPOS64_T offset64; if (file==NULL) return 0; //UNZ_PARAMERROR; offset64 = unzGetOffset64(file); return (uLong)offset64; } extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) { return unzSetOffset64(file,pos); } wordgrinder-0.5.1.orig/src/c/minizip/unzip.h0000644000000000000000000003774612121126227015713 0ustar /* unzip.h -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------------- Changes See header of unzip64.c */ #ifndef _unz64_H #define _unz64_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info64_s { ZPOS64_T number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info64; typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info64_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ ZPOS64_T compressed_size; /* compressed size 8 bytes */ ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info64; typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, const char* fileName2, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen OF((const char *path)); extern unzFile ZEXPORT unzOpen64 OF((const void *path)); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. the "64" function take a const void* pointer, because the path is just the value passed to the open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */ extern unzFile ZEXPORT unzOpen2 OF((const char *path, zlib_filefunc_def* pzlib_filefunc_def)); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, zlib_filefunc64_def* pzlib_filefunc_def)); /* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose OF((unzFile file)); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info *pglobal_info)); extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, unz_global_info64 *pglobal_info)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char *szComment, uLong uSizeBuf)); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile OF((unzFile file, const char *szFileName, int iCaseSensitivity)); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); typedef struct unz64_file_pos_s { ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ ZPOS64_T num_of_file; /* # of file */ } unz64_file_pos; extern int ZEXPORT unzGetFilePos64( unzFile file, unz64_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos64( unzFile file, const unz64_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); /** Addition for GDAL : END */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password)); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len)); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell OF((unzFile file)); extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof OF((unzFile file)); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len)); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz64_H */ wordgrinder-0.5.1.orig/src/c/minizip/zip.c0000644000000000000000000020047212132346214015331 0ustar /* zip.c -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Mathias Svensson - Remove old C style function prototypes Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data It is used when recreting zip archive with RAW when deleting items from a zip. ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer */ #include #include #include #include #include "zlib.h" #include "zip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY # define VERSIONMADEBY (0x0) /* platform depedent */ #endif #ifndef Z_BUFSIZE #define Z_BUFSIZE (64*1024) //(16384) #endif #ifndef Z_MAXFILENAMEINZIP #define Z_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif /* #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) */ /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ // NOT sure that this work on ALL platform #define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef DEF_MEM_LEVEL #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #endif const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; #define SIZEDATA_INDATABLOCK (4096-(4*4)) #define LOCALHEADERMAGIC (0x04034b50) #define CENTRALHEADERMAGIC (0x02014b50) #define ENDHEADERMAGIC (0x06054b50) #define ZIP64ENDHEADERMAGIC (0x6064b50) #define ZIP64ENDLOCHEADERMAGIC (0x7064b50) #define FLAG_LOCALHEADER_OFFSET (0x06) #define CRC_LOCALHEADER_OFFSET (0x0e) #define SIZECENTRALHEADER (0x2e) /* 46 */ typedef struct linkedlist_datablock_internal_s { struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; uLong unused; /* for future use and alignement */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; typedef struct linkedlist_data_s { linkedlist_datablock_internal* first_block; linkedlist_datablock_internal* last_block; } linkedlist_data; typedef struct { z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif int stream_initialised; /* 1 is stream is initialised */ uInt pos_in_buffered_data; /* last written byte in buffered_data */ ZPOS64_T pos_local_header; /* offset of the local header of the file currenty writing */ char* central_header; /* central header data for the current file */ uLong size_centralExtra; uLong size_centralheader; /* size of the central header for cur file */ uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ uLong flag; /* flag of the file currently writing */ int method; /* compression method of file currenty wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; int zip64; /* Add ZIP64 extened information in the extra field */ ZPOS64_T pos_zip64extrainfo; ZPOS64_T totalCompressedData; ZPOS64_T totalUncompressedData; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; int crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ curfile64_info ci; /* info on the file curretly writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ ZPOS64_T add_position_when_writting_offset; ZPOS64_T number_entry; #ifndef NO_ADDFILEINEXISTINGZIP char *globalcomment; #endif } zip64_internal; #ifndef NOCRYPT #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED #include "crypt.h" #endif local linkedlist_datablock_internal* allocate_new_datablock() { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); if (ldi!=NULL) { ldi->next_datablock = NULL ; ldi->filled_in_this_block = 0 ; ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; } return ldi; } local void free_datablock(linkedlist_datablock_internal* ldi) { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; TRYFREE(ldi); ldi = ldinext; } } local void init_linkedlist(linkedlist_data* ll) { ll->first_block = ll->last_block = NULL; } local void free_linkedlist(linkedlist_data* ll) { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; if (ll==NULL) return ZIP_INTERNALERROR; if (ll->last_block == NULL) { ll->first_block = ll->last_block = allocate_new_datablock(); if (ll->first_block == NULL) return ZIP_INTERNALERROR; } ldi = ll->last_block; from_copy = (unsigned char*)buf; while (len>0) { uInt copy_this; uInt i; unsigned char* to_copy; if (ldi->avail_in_this_block==0) { ldi->next_datablock = allocate_new_datablock(); if (ldi->next_datablock == NULL) return ZIP_INTERNALERROR; ldi = ldi->next_datablock ; ll->last_block = ldi; } if (ldi->avail_in_this_block < len) copy_this = (uInt)ldi->avail_in_this_block; else copy_this = (uInt)len; to_copy = &(ldi->data[ldi->filled_in_this_block]); for (i=0;ifilled_in_this_block += copy_this; ldi->avail_in_this_block -= copy_this; from_copy += copy_this ; len -= copy_this; } return ZIP_OK; } /****************************************************************************/ #ifndef NO_ADDFILEINEXISTINGZIP /* =========================================================================== Inputs a long in LSB order to the given file nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) */ local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) { unsigned char buf[8]; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 (X Roche) */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } } /****************************************************************************/ local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) { uLong year = (uLong)ptm->tm_year; if (year>=1980) year-=1980; else if (year>=80) year-=80; return (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return ZIP_OK; } else { if (ZERROR64(*pzlib_filefunc_def,filestream)) return ZIP_ERRNO; else return ZIP_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (ZPOS64_T)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<24; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<32; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<40; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<48; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<56; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) { // Signature "0x07064b50" Zip64 end of central directory locater if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+i; break; } } if (uPosFound!=0) break; } TRYFREE(buf); if (uPosFound == 0) return 0; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature, already checked */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) return 0; /* relative offset of the zip64 end of central directory record */ if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) return 0; /* total number of disks */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 1) return 0; /* Goto Zip64 end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' return 0; return relativeOffset; } int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory */ ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ uLong VersionMadeBy; uLong VersionNeeded; uLong size_comment; int hasZIP64Record = 0; // check first if we find a ZIP64 record central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); if(central_pos > 0) { hasZIP64Record = 1; } else if(central_pos == 0) { central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); } /* disable to allow appending to empty ZIP archive if (central_pos==0) err=ZIP_ERRNO; */ if(hasZIP64Record) { ZPOS64_T sizeEndOfCentralDirectory; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* size of zip64 end of central directory record */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) err=ZIP_ERRNO; /* version made by */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) err=ZIP_ERRNO; /* version needed to extract */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory on this disk */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) err=ZIP_ERRNO; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) err=ZIP_ERRNO; // TODO.. // read the comment from the standard central header. size_comment = 0; } else { // Read End of central Directory info if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central dir on this disk */ number_entry = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry = uL; /* total number of entries in the central dir */ number_entry_CD = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry_CD = uL; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ size_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ offset_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else offset_central_dir = uL; /* zipfile global comment length */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) err=ZIP_ERRNO; } if ((central_posz_filefunc, pziinit->filestream); return ZIP_ERRNO; } if (size_comment>0) { pziinit->globalcomment = (char*)ALLOC(size_comment+1); if (pziinit->globalcomment) { size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); pziinit->globalcomment[size_comment]=0; } } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); pziinit->add_position_when_writting_offset = byte_before_the_zipfile; { ZPOS64_T size_central_dir_to_read = size_central_dir; size_t buf_size = SIZEDATA_INDATABLOCK; void* buf_read = (void*)ALLOC(buf_size); if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; while ((size_central_dir_to_read>0) && (err==ZIP_OK)) { ZPOS64_T read_this = SIZEDATA_INDATABLOCK; if (read_this > size_central_dir_to_read) read_this = size_central_dir_to_read; if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) err=ZIP_ERRNO; if (err==ZIP_OK) err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); size_central_dir_to_read-=read_this; } TRYFREE(buf_read); } pziinit->begin_pos = byte_before_the_zipfile; pziinit->number_entry = number_entry_CD; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; return err; } #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; int err=ZIP_OK; ziinit.z_filefunc.zseek32_file = NULL; ziinit.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); else ziinit.z_filefunc = *pzlib_filefunc64_32_def; ziinit.filestream = ZOPEN64(ziinit.z_filefunc, pathname, (append == APPEND_STATUS_CREATE) ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); if (ziinit.filestream == NULL) return NULL; if (append == APPEND_STATUS_CREATEAFTER) ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; ziinit.add_position_when_writting_offset = 0; init_linkedlist(&(ziinit.central_dir)); zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); if (zi==NULL) { ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); return NULL; } /* now we add file in a zipfile */ # ifndef NO_ADDFILEINEXISTINGZIP ziinit.globalcomment = NULL; if (append == APPEND_STATUS_ADDINZIP) { // Read and Cache Central Directory Records err = LoadCentralDirectoryRecord(&ziinit); } if (globalcomment) { *globalcomment = ziinit.globalcomment; } # endif /* !NO_ADDFILEINEXISTINGZIP*/ if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ TRYFREE(zi); return NULL; } else { *zi = ziinit; return (zipFile)zi; } } extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(pathname, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; zlib_filefunc64_32_def_fill.ztell32_file = NULL; zlib_filefunc64_32_def_fill.zseek32_file = NULL; return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(pathname, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen (const char* pathname, int append) { return zipOpen3((const void*)pathname,append,NULL,NULL); } extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) { return zipOpen3(pathname,append,NULL,NULL); } int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) { /* write the local header */ int err; uInt size_filename = (uInt)strlen(filename); uInt size_extrafield = size_extrafield_local; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ } if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); if(zi->ci.zip64) { size_extrafield += 20; } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); if ((err==ZIP_OK) && (size_filename > 0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (size_extrafield_local > 0)) { if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (zi->ci.zip64)) { // write the Zip64 extended info short HeaderID = 1; short DataSize = 16; ZPOS64_T CompressedSize = 0; ZPOS64_T UncompressedSize = 0; // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); } return err; } /* NOTE. When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped before calling this function it can be done with zipRemoveExtraInfoBlock It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize unnecessary allocations. */ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64) { zip64_internal* zi; uInt size_filename; uInt size_comment; uInt i; int err = ZIP_OK; # ifdef NOCRYPT (crcForCrypting); if (password != NULL) return ZIP_PARAMERROR; # endif if (file == NULL) return ZIP_PARAMERROR; #ifdef HAVE_BZIP2 if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) return ZIP_PARAMERROR; #else if ((method!=0) && (method!=Z_DEFLATED)) return ZIP_PARAMERROR; #endif zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); if (err != ZIP_OK) return err; } if (filename==NULL) filename="-"; if (comment==NULL) size_comment = 0; else size_comment = (uInt)strlen(comment); size_filename = (uInt)strlen(filename); if (zipfi == NULL) zi->ci.dosDate = 0; else { if (zipfi->dosDate != 0) zi->ci.dosDate = zipfi->dosDate; else zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); } zi->ci.flag = flagBase; if ((level==8) || (level==9)) zi->ci.flag |= 2; if (level==2) zi->ci.flag |= 4; if (level==1) zi->ci.flag |= 6; if (password != NULL) zi->ci.flag |= 1; zi->ci.crc32 = 0; zi->ci.method = method; zi->ci.encrypt = 0; zi->ci.stream_initialised = 0; zi->ci.pos_in_buffered_data = 0; zi->ci.raw = raw; zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); zi->ci.size_centralExtra = size_extrafield_global; zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); else zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); else zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); if(zi->ci.pos_local_header >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); else zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = *(((const char*)extrafield_global)+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ size_extrafield_global+i) = *(comment+i); if (zi->ci.central_header == NULL) return ZIP_INTERNALERROR; zi->ci.zip64 = zip64; zi->ci.totalCompressedData = 0; zi->ci.totalUncompressedData = 0; zi->ci.pos_zip64extrainfo = 0; err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); #ifdef HAVE_BZIP2 zi->ci.bstream.avail_in = (uInt)0; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; zi->ci.bstream.total_in_hi32 = 0; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_out_hi32 = 0; zi->ci.bstream.total_out_lo32 = 0; #endif zi->ci.stream.avail_in = (uInt)0; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; zi->ci.stream.data_type = Z_BINARY; #ifdef HAVE_BZIP2 if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) #else if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) #endif { if(zi->ci.method == Z_DEFLATED) { zi->ci.stream.zalloc = (alloc_func)0; zi->ci.stream.zfree = (free_func)0; zi->ci.stream.opaque = (voidpf)0; if (windowBits>0) windowBits = -windowBits; err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); if (err==Z_OK) zi->ci.stream_initialised = Z_DEFLATED; } else if(zi->ci.method == Z_BZIP2ED) { #ifdef HAVE_BZIP2 // Init BZip stuff here zi->ci.bstream.bzalloc = 0; zi->ci.bstream.bzfree = 0; zi->ci.bstream.opaque = (voidpf)0; err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); if(err == BZ_OK) zi->ci.stream_initialised = Z_BZIP2ED; #endif } } # ifndef NOCRYPT zi->ci.crypt_header_size = 0; if ((err==Z_OK) && (password != NULL)) { unsigned char bufHead[RAND_HEAD_LEN]; unsigned int sizeHead; zi->ci.encrypt = 1; zi->ci.pcrc_32_tab = get_crc_table(); /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); zi->ci.crypt_header_size = sizeHead; if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) err = ZIP_ERRNO; } # endif if (err==Z_OK) zi->in_opened_file_inzip = 1; return err; } extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, versionMadeBy, flagBase, 0); } extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } local int zip64FlushWriteBuffer(zip64_internal* zi) { int err=ZIP_OK; if (zi->ci.encrypt != 0) { #ifndef NOCRYPT uInt i; int t; for (i=0;ici.pos_in_buffered_data;i++) zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); #endif } if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) err = ZIP_ERRNO; zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED) { zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_in_hi32 = 0; } else #endif { zi->ci.totalUncompressedData += zi->ci.stream.total_in; zi->ci.stream.total_in = 0; } zi->ci.pos_in_buffered_data = 0; return err; } extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) { zip64_internal* zi; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) { zi->ci.bstream.next_in = (void*)buf; zi->ci.bstream.avail_in = len; err = BZ_RUN_OK; while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) { if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } if(err != BZ_RUN_OK) break; if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; // uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; } } if(err == BZ_RUN_OK) err = ZIP_OK; } else #endif { zi->ci.stream.next_in = (Bytef*)buf; zi->ci.stream.avail_in = len; while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } if(err != ZIP_OK) break; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); if(uTotalOutBefore > zi->ci.stream.total_out) { int bBreak = 0; bBreak++; } zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } else { uInt copy_this,i; if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) copy_this = zi->ci.stream.avail_in; else copy_this = zi->ci.stream.avail_out; for (i = 0; i < copy_this; i++) *(((char*)zi->ci.stream.next_out)+i) = *(((const char*)zi->ci.stream.next_in)+i); { zi->ci.stream.avail_in -= copy_this; zi->ci.stream.avail_out-= copy_this; zi->ci.stream.next_in+= copy_this; zi->ci.stream.next_out+= copy_this; zi->ci.stream.total_in+= copy_this; zi->ci.stream.total_out+= copy_this; zi->ci.pos_in_buffered_data += copy_this; } } }// while(...) } return err; } extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) { return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); } extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) { zip64_internal* zi; ZPOS64_T compressed_size; uLong invalidValue = 0xffffffff; short datasize = 0; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.stream.avail_in = 0; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { while (err==ZIP_OK) { uLong uTotalOutBefore; if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_FINISH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } } else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { #ifdef HAVE_BZIP2 err = BZ_FINISH_OK; while (err==BZ_FINISH_OK) { uLong uTotalOutBefore; if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } uTotalOutBefore = zi->ci.bstream.total_out_lo32; err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); if(err == BZ_STREAM_END) err = Z_STREAM_END; zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); } if(err == BZ_FINISH_OK) err = ZIP_OK; #endif } if (err==Z_STREAM_END) err=ZIP_OK; /* this is normal */ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) { if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) err = ZIP_ERRNO; } if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { int tmp_err = deflateEnd(&zi->ci.stream); if (err == ZIP_OK) err = tmp_err; zi->ci.stream_initialised = 0; } #ifdef HAVE_BZIP2 else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); if (err==ZIP_OK) err = tmperr; zi->ci.stream_initialised = 0; } #endif if (!zi->ci.raw) { crc32 = (uLong)zi->ci.crc32; uncompressed_size = zi->ci.totalUncompressedData; } compressed_size = zi->ci.totalCompressedData; # ifndef NOCRYPT compressed_size += zi->ci.crypt_header_size; # endif // update Current Item crc and sizes, if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) { /*version Made by*/ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); /*version needed*/ zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); } zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ if(compressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ /// set internal file attributes field if (zi->ci.stream.data_type == Z_ASCII) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); if(uncompressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ // Add ZIP64 extra info field for uncompressed size if(uncompressed_size >= 0xffffffff) datasize += 8; // Add ZIP64 extra info field for compressed size if(compressed_size >= 0xffffffff) datasize += 8; // Add ZIP64 extra info field for relative offset to local file header of current file if(zi->ci.pos_local_header >= 0xffffffff) datasize += 8; if(datasize > 0) { char* p = NULL; if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { // we can not write more data to the buffer that we have room for. return ZIP_BADZIPFILE; } p = zi->ci.central_header + zi->ci.size_centralheader; // Add Extra Information Header for 'ZIP64 information' zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID p += 2; zip64local_putValue_inmemory(p, datasize, 2); // DataSize p += 2; if(uncompressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, uncompressed_size, 8); p += 8; } if(compressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, compressed_size, 8); p += 8; } if(zi->ci.pos_local_header >= 0xffffffff) { zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); p += 8; } // Update how much extra free space we got in the memory buffer // and increase the centralheader size so the new ZIP64 fields are included // ( 4 below is the size of HeaderID and DataSize field ) zi->ci.size_centralExtraFree -= datasize + 4; zi->ci.size_centralheader += datasize + 4; // Update the extra info size field zi->ci.size_centralExtra += datasize + 4; zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); } if (err==ZIP_OK) err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); free(zi->ci.central_header); if (err==ZIP_OK) { // Update the LocalFileHeader with the new values. ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) { if(zi->ci.pos_zip64extrainfo > 0) { // Update the size in the ZIP64 extended field. if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); } else err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal } else { if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); } if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; } zi->number_entry ++; zi->in_opened_file_inzip = 0; return err; } extern int ZEXPORT zipCloseFileInZip (zipFile file) { return zipCloseFileInZipRaw (file,0,0); } int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); /*num disks*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /*relative offset*/ if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); return err; } int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; uLong Zip64DataSize = 44; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? if (err==ZIP_OK) /* version made by */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* version needed */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* total number of entries in the central dir */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; /*signature*/ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ { { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } } if (err==ZIP_OK) /* total number of entries in the central dir */ { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; if(pos >= 0xffffffff) { err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); } else err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); } return err; } int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; if(global_comment != NULL) size_global_comment = (uInt)strlen(global_comment); err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); if (err == ZIP_OK && size_global_comment > 0) { if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) err = ZIP_ERRNO; } return err; } extern int ZEXPORT zipClose (zipFile file, const char* global_comment) { zip64_internal* zi; int err = 0; uLong size_centraldir = 0; ZPOS64_T centraldir_pos_inzip; ZPOS64_T pos; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); } #ifndef NO_ADDFILEINEXISTINGZIP if (global_comment==NULL) global_comment = zi->globalcomment; #endif centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (err==ZIP_OK) { linkedlist_datablock_internal* ldi = zi->central_dir.first_block; while (ldi!=NULL) { if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) err = ZIP_ERRNO; } size_centraldir += ldi->filled_in_this_block; ldi = ldi->next_datablock; } } free_linkedlist(&(zi->central_dir)); pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); } if (err==ZIP_OK) err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); if(err == ZIP_OK) err = Write_GlobalComment(zi, global_comment); if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) if (err == ZIP_OK) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(zi->globalcomment); #endif TRYFREE(zi); return err; } extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) { char* p = pData; int size = 0; char* pNewHeader; char* pTmp; short header; short dataSize; int retVal = ZIP_OK; if(pData == NULL || *dataLen < 4) return ZIP_PARAMERROR; pNewHeader = (char*)ALLOC(*dataLen); pTmp = pNewHeader; while(p < (pData + *dataLen)) { header = *(short*)p; dataSize = *(((short*)p)+1); if( header == sHeader ) // Header found. { p += dataSize + 4; // skip it. do not copy to temp buffer } else { // Extra Info block should not be removed, So copy it to the temp buffer. memcpy(pTmp, p, dataSize + 4); p += dataSize + 4; size += dataSize + 4; } } if(size < *dataLen) { // clean old extra info block. memset(pData,0, *dataLen); // copy the new extra info block over the old if(size > 0) memcpy(pData, pNewHeader, size); // set the new extra info size *dataLen = size; retVal = ZIP_OK; } else retVal = ZIP_ERRNO; TRYFREE(pNewHeader); return retVal; } wordgrinder-0.5.1.orig/src/c/minizip/zip.h0000644000000000000000000003600612121126227015334 0ustar /* zip.h -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------- Changes See header of zip.h */ #ifndef _zip12_H #define _zip12_H #ifdef __cplusplus extern "C" { #endif //#define HAVE_BZIP2 #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagzipFile__ { int unused; } zipFile__; typedef zipFile__ *zipFile; #else typedef voidp zipFile; #endif #define ZIP_OK (0) #define ZIP_EOF (0) #define ZIP_ERRNO (Z_ERRNO) #define ZIP_PARAMERROR (-102) #define ZIP_BADZIPFILE (-103) #define ZIP_INTERNALERROR (-104) #ifndef DEF_MEM_LEVEL # if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 # else # define DEF_MEM_LEVEL MAX_MEM_LEVEL # endif #endif /* default memLevel */ /* tm_zip contain date/time info */ typedef struct tm_zip_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct { tm_zip tmz_date; /* date in understandable format */ uLong dosDate; /* if dos_date == 0, tmu_date is used */ /* uLong flag; */ /* general purpose bit flag 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ } zip_fileinfo; typedef const char* zipcharpc; #define APPEND_STATUS_CREATE (0) #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip will be created at the end of the file. (useful if the file contain a self extractor code) if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will add files in existing zip (be sure you don't add file that doesn't exist) If the zipfile cannot be opened, the return value is NULL. Else, the return value is a zipFile Handle, usable with other function of this zip package. */ /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another Of couse, you can use RAW reading and writing to copy the file you did not want delte */ extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)); extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level)); extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64)); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local contains the extrafield data the the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data the the local header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) zip64 is set to 1 if a zip64 extended information block should be added to the local file header. this MUST be '1' if the uncompressed size is >= 0xffffffff. */ extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw)); extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64)); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting)); extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64 )); /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 password : crypting password (NULL for no crypting) crcForCrypting : crc of file to compress (needed for crypting) */ extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase )); extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64 )); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field flag : value for flag field (compression level info will be added) */ extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, unsigned len)); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, uLong uncompressed_size, uLong crc32)); extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, ZPOS64_T uncompressed_size, uLong crc32)); /* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2 uncompressed_size and crc32 are value for the uncompressed size */ extern int ZEXPORT zipClose OF((zipFile file, const char* global_comment)); /* Close the zipfile */ extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson Remove extra information block from a extra information data for the local file header or central directory header It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. 0x0001 is the signature header for the ZIP64 extra information blocks usage. Remove ZIP64 Extra information from a central director extra field data zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); Remove ZIP64 Extra information from a Local File Header extra field data zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); */ #ifdef __cplusplus } #endif #endif /* _zip64_H */ wordgrinder-0.5.1.orig/src/c/screen.c0000644000000000000000000001056612243254677014350 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include "globals.h" #include static bool running = false; static int cursorx = 0; static int cursory = 0; void screen_deinit(void) { if (running) { dpy_shutdown(); running = false; } } static int initscreen_cb(lua_State* L) { dpy_start(); running = true; atexit(screen_deinit); return 0; } static int clearscreen_cb(lua_State* L) { dpy_clearscreen(); return 0; } static int sync_cb(lua_State* L) { dpy_setcursor(cursorx, cursory); dpy_sync(); return 0; } static int setbold_cb(lua_State* L) { dpy_setattr(-1, DPY_BOLD); return 0; } static int setunderline_cb(lua_State* L) { dpy_setattr(-1, DPY_UNDERLINE); return 0; } static int setreverse_cb(lua_State* L) { dpy_setattr(-1, DPY_REVERSE); return 0; } static int setdim_cb(lua_State* L) { dpy_setattr(-1, DPY_DIM); return 0; } static int setbright_cb(lua_State* L) { dpy_setattr(-1, DPY_BRIGHT); return 0; } static int setitalic_cb(lua_State* L) { dpy_setattr(-1, DPY_ITALIC); return 0; } static int setnormal_cb(lua_State* L) { dpy_setattr(0, 0); return 0; } static int write_cb(lua_State* L) { int x = luaL_checkint(L, 1); int y = luaL_checkint(L, 2); size_t size; const char* s = luaL_checklstring(L, 3, &size); const char* send = s + size; while (s < send) { wchar_t c = readu8(&s); dpy_writechar(x, y, c); if (!iswcntrl(c)) x += emu_wcwidth(c); } return 0; } static int cleararea_cb(lua_State* L) { int x1 = luaL_checkint(L, 1); int y1 = luaL_checkint(L, 2); int x2 = luaL_checkint(L, 3); int y2 = luaL_checkint(L, 4); dpy_cleararea(x1, y1, x2, y2); return 0; } static int gotoxy_cb(lua_State* L) { cursorx = luaL_checkint(L, 1); cursory = luaL_checkint(L, 2); return 0; } static int getscreensize_cb(lua_State* L) { int x, y; dpy_getscreensize(&x, &y); lua_pushnumber(L, x); lua_pushnumber(L, y); return 2; } static int getstringwidth_cb(lua_State* L) { size_t size; const char* s = luaL_checklstring(L, 1, &size); const char* send = s + size; int width = 0; while (s < send) { wchar_t c = readu8(&s); if (!iswcntrl(c)) width += emu_wcwidth(c); } lua_pushnumber(L, width); return 1; } static int getboundedstring_cb(lua_State* L) { size_t size; const char* start = luaL_checklstring(L, 1, &size); const char* send = start + size; int width = luaL_checkinteger(L, 2); const char* s = start; while (s < send) { const char* p = s; wchar_t c = readu8(&s); if (!iswcntrl(c)) { width -= emu_wcwidth(c); if (width < 0) { send = p; break; } } } lua_pushlstring(L, start, send - start); return 1; } static int getbytesofcharacter_cb(lua_State* L) { int c = luaL_checkinteger(L, 1); lua_pushnumber(L, getu8bytes(c)); return 1; } static int getchar_cb(lua_State* L) { int t = -1; if (!lua_isnone(L, 1)) t = luaL_checkinteger(L, 1); dpy_setcursor(cursorx, cursory); dpy_sync(); for (;;) { uni_t c = dpy_getchar(t); if (c <= 0) { const char* s = dpy_getkeyname(c); if (s) { lua_pushstring(L, s); break; } } if (emu_wcwidth(c) > 0) { static char buffer[8]; char* p = buffer; writeu8(&p, c); *p = '\0'; lua_pushstring(L, buffer); break; } } return 1; } void screen_init(const char* argv[]) { dpy_init(argv); const static luaL_Reg funcs[] = { { "initscreen", initscreen_cb }, { "clearscreen", clearscreen_cb }, { "sync", sync_cb }, { "setbold", setbold_cb }, { "setunderline", setunderline_cb }, { "setreverse", setreverse_cb }, { "setbright", setbright_cb }, { "setdim", setdim_cb }, { "setitalic", setitalic_cb }, { "setnormal", setnormal_cb }, { "write", write_cb }, { "cleararea", cleararea_cb }, { "gotoxy", gotoxy_cb }, { "getscreensize", getscreensize_cb }, { "getstringwidth", getstringwidth_cb }, { "getboundedstring", getboundedstring_cb }, { "getbytesofcharacter", getbytesofcharacter_cb }, { "getchar", getchar_cb }, { NULL, NULL } }; lua_getglobal(L, "wg"); luaL_setfuncs(L, funcs, 0); } wordgrinder-0.5.1.orig/src/c/utils.c0000644000000000000000000001116112243254677014221 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include "globals.h" #include static const uint8_t masks[6] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; static const signed char trailing_bytes[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 8 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 9 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // A -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // E 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, // F }; int getu8bytes(char c) { return trailing_bytes[(unsigned char) c] + 1; } uni_t readu8(const char** srcp) { const char* src = *srcp; int nb = trailing_bytes[*(unsigned char*)src]; if (nb == -1) { /* Invalid character! */ (*srcp)++; return 0xfffd; } uni_t ch = (unsigned char)*src++ & masks[nb]; switch (nb) { /* these fall through deliberately */ case 5: ch <<= 6; ch += (unsigned char)*src++ & 0x3f; case 4: ch <<= 6; ch += (unsigned char)*src++ & 0x3f; case 3: ch <<= 6; ch += (unsigned char)*src++ & 0x3f; case 2: ch <<= 6; ch += (unsigned char)*src++ & 0x3f; case 1: ch <<= 6; ch += (unsigned char)*src++ & 0x3f; case 0: break; } *srcp = src; return ch; } void writeu8(char** destp, uni_t ch) { char* dest = *destp; if (ch < 0) assert(false); else if (ch < 0x80) { *dest++ = (char)ch; } else if (ch < 0x800) { *dest++ = (ch>>6) | 0xC0; *dest++ = (ch & 0x3F) | 0x80; } else if (ch < 0x10000) { *dest++ = (ch>>12) | 0xE0; *dest++ = ((ch>>6) & 0x3F) | 0x80; *dest++ = (ch & 0x3F) | 0x80; } else if (ch < 0x200000) { *dest++ = (ch>>18) | 0xF0; *dest++ = ((ch>>12) & 0x3F) | 0x80; *dest++ = ((ch>>6) & 0x3F) | 0x80; *dest++ = (ch & 0x3F) | 0x80; } else if (ch < 0x4000000) { *dest++ = (ch>>24) | 0xF8; *dest++ = ((ch>>18) & 0x3F) | 0x80; *dest++ = ((ch>>12) & 0x3F) | 0x80; *dest++ = ((ch>>6) & 0x3F) | 0x80; *dest++ = (ch & 0x3F) | 0x80; } else if (ch <= 0x7fffffff) { *dest++ = (ch>>30) | 0xFC; *dest++ = ((ch>>24) & 0x3F) | 0x80; *dest++ = ((ch>>18) & 0x3F) | 0x80; *dest++ = ((ch>>12) & 0x3F) | 0x80; *dest++ = ((ch>>6) & 0x3F) | 0x80; *dest++ = (ch & 0x3F) | 0x80; } *destp = dest; } static int readu8_cb(lua_State* L) { const char* s = luaL_checkstring(L, 1); int offset = lua_tointeger(L, 2); uni_t c; if (offset > 0) offset--; const char* p = s + offset; c = readu8(&p); lua_pushnumber(L, c); lua_pushinteger(L, (p - s)+1); return 2; } static int writeu8_cb(lua_State* L) { uni_t c = luaL_checkinteger(L, 1); static char buffer[8]; char* s = buffer; writeu8(&s, c); lua_pushlstring(L, buffer, s-buffer); return 1; } static int transcode_cb(lua_State* L) { size_t inputbuffersize; const char* inputbuffer = luaL_checklstring(L, 1, &inputbuffersize); size_t outputbuffersize = inputbuffersize*2 + 32; char* outputbuffer = malloc(outputbuffersize); /* should fit everything */ const char* in = (char*) inputbuffer; const char* inend = inputbuffer + inputbuffersize; char* out = outputbuffer; //char* outend = outputbuffer + outputbuffersize - 4; while (in < inend) { int c = readu8(&in); writeu8(&out, c); } lua_pushlstring(L, outputbuffer, out - outputbuffer); free(outputbuffer); return 1; } static int time_cb(lua_State* L) { struct timeval tv; gettimeofday(&tv, NULL); double t = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; lua_pushnumber(L, t); return 1; } void utils_init(void) { const static luaL_Reg funcs[] = { { "readu8", readu8_cb }, { "writeu8", writeu8_cb }, { "transcode", transcode_cb }, { "time", time_cb }, { NULL, NULL } }; lua_getglobal(L, "wg"); luaL_setfuncs(L, funcs, 0); } wordgrinder-0.5.1.orig/src/c/word.c0000644000000000000000000002070212243254677014035 0ustar /* © 2008 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include "globals.h" #include /* A 'word' is a string with embedded text style codes. * * The data consists of standard UTF-8 sequences interspaced with * control codes. A word is always considered to start with * all style turned off. * * Control codes consist of combinations of the first four DPY_ * constants, with STYLE_MARKER set to prevent nil characters. */ enum { STYLE_MARKER = (1<<4), /* always set */ STYLE_ALL = 15 }; #define OVERHEAD (3*2 + 1) /* Parse a styled word. */ static int parseword_cb(lua_State* L) { size_t size; const char* s = luaL_checklstring(L, 1, &size); const char* send = s + size; int dstyle = luaL_checkint(L, 2); /* pos 3 contains the callback function */ int oldattr = 0; int attr = 0; const char* w = s; const char* wend = NULL; bool flush = false; for (;;) { if (s == send) { wend = s; flush = true; } if (flush) { if (w != wend) { lua_pushvalue(L, 3); lua_pushnumber(L, oldattr | dstyle); lua_pushlstring(L, w, wend - w); lua_call(L, 2, 0); } w = s; oldattr = attr; flush = false; } if (s == send) break; wchar_t c = readu8(&s); if (iswcntrl(c)) { oldattr = attr; attr = c & STYLE_ALL; flush = true; wend = s - 1; } } return 0; } /* Draw a styled word at a particular location. */ static int writestyled_cb(lua_State* L) { int x = luaL_checkint(L, 1); int y = luaL_checkint(L, 2); size_t size; const char* s = luaL_checklstring(L, 3, &size); const char* send = s + size; int oattr = luaL_checkint(L, 4); const char* revon = s + lua_tointeger(L, 5) - 1; const char* revoff = s + lua_tointeger(L, 6) - 1; int sor = lua_tointeger(L, 7); int attr = sor; int mark = 0; dpy_setattr(0, sor); bool first = true; while (s < send) { if (s == revon) { mark = DPY_REVERSE; dpy_setattr(0, attr | mark); } if (s == revoff) { mark = 0; dpy_setattr(0, attr | mark); } wchar_t c = readu8(&s); if (iswcntrl(c)) { c &= STYLE_ALL; attr = c | sor; dpy_setattr(0, attr | mark); } else { if (first && oattr && ((attr | mark) == oattr)) dpy_writechar(x-1, y, 160); /* non-breaking space */ dpy_writechar(x, y, c); x += emu_wcwidth(c); first = false; } } dpy_setattr(0, 0); lua_pushnumber(L, attr | mark); return 1; } /* Returns the raw text of a word, with no styling. */ static int getwordtext_cb(lua_State* L) { size_t bytes; const char* src = luaL_checklstring(L, 1, &bytes); char dest[bytes+1]; char* p = dest; for (;;) { int c = *src++; if (c == '\0') break; if (!iscntrl(c)) *p++ = c; } lua_pushlstring(L, dest, p - dest); return 1; } /* Advances an offset pointer to the next thing in the string. */ static int nextcharinword_cb(lua_State* L) { const char* src = luaL_checkstring(L, 1); int offset = luaL_checkint(L, 2) - 1; const char* p = src + offset; /* At the end of the string? */ if (*p == '\0') { atend: lua_pushnil(L); return 1; } /* Skip any control codes. */ while (*p && iscntrl(*p)) p++; if (*p == '\0') goto atend; /* Skip exactly one UTF-8 code point. */ (void) readu8(&p); lua_pushnumber(L, 1 + p - src); return 1; } /* Backs up to the previous thing in a string. */ static int prevcharinword_cb(lua_State* L) { const char* src = luaL_checkstring(L, 1); int offset = luaL_checkint(L, 2) - 1; const char* p = src + offset; /* At the beginning of the string? */ if (p == src) { lua_pushnil(L); return 1; } /* Back up, and then skip any UTF-8 trailing bytes. */ p--; while ((p != src) && (getu8bytes(*p) == 0)) p--; /* Back up over any control codes. */ while (p != src) { if (iscntrl(*(p-1))) p--; else break; } lua_pushnumber(L, 1 + p - src); return 1; } /* Copies one character (or control code) from the source to the destination * string. */ static bool copy(char** dest, int* dstate, const char** src, int* sstate, int stateor, int stateand) { const char* oldsrc = *src; int c = readu8(src); if (c == '\0') return false; if (iswcntrl(c)) { *sstate = c & STYLE_ALL; return true; } /* If we got here, we've just read a printable character. */ if (dest) { int estate = (*sstate & stateand) | stateor; if (*dstate != estate) { /* We need to emit a style change byte; we do this, then we * back up over the printable character we just read, so we * can read it again next time. */ writeu8(dest, estate | STYLE_MARKER); *dstate = estate; *src = oldsrc; return true; } writeu8(dest, c); } return true; } /* Inserts a word into another word, at a particular offset. */ static int insertintoword_cb(lua_State* L) { size_t srcbytes; const char* src = luaL_checklstring(L, 1, &srcbytes); const char* s = src; size_t insbytes; const char* ins = luaL_checklstring(L, 2, &insbytes); int offset = luaL_checkint(L, 3) - 1; char dest[srcbytes + insbytes + OVERHEAD]; char* p = dest; int sstate = 0; int dstate = 0; int insend = -1; bool copied = 0; do { /* If we reach the right point in the source string, copy in the * destination string. */ if (!copied && ((s - src) >= offset)) { int ss = 0; while (copy(&p, &dstate, &ins, &ss, 0, STYLE_ALL)) ; insend = p - dest; copied = 1; } } while (copy(&p, &dstate, &s, &sstate, 0, STYLE_ALL)); /* Return both the new string, and the offset to the end of the inserted * section. */ lua_pushlstring(L, dest, p - dest); if (insend != -1) lua_pushnumber(L, 1 + insend); else lua_pushnil(L); return 2; } /* Deletes all characters between certain offsets in a word. */ static int deletefromword_cb(lua_State* L) { size_t srcbytes; const char* src = luaL_checklstring(L, 1, &srcbytes); const char* offset1 = src + luaL_checkint(L, 2) - 1; const char* offset2 = src + luaL_checkint(L, 3) - 1; char dest[srcbytes]; char* p = dest; int sstate = 0; int dstate = 0; do { /* If we reach the right point in the source string, skip characters * until we reach the end offset. */ if (src == offset1) { while (src < offset2) { if (!copy(NULL, &dstate, &src, &sstate, 0, STYLE_ALL)) goto finished; } } } while (copy(&p, &dstate, &src, &sstate, 0, STYLE_ALL)); finished: lua_pushlstring(L, dest, p - dest); return 1; } /* Turns on or off a style to a particular range of a word. */ static int applystyletoword_cb(lua_State* L) { size_t srcbytes; const char* src = luaL_checklstring(L, 1, &srcbytes); int targetsor = luaL_checkint(L, 2); int targetsand = luaL_checkint(L, 3); const char* offset1 = src + luaL_checkint(L, 4) - 1; const char* offset2 = src + luaL_checkint(L, 5) - 1; const char* csoffset = src + luaL_checkint(L, 6) - 1; char dest[srcbytes]; char* p = dest; char* cdoffset = dest; int sand = STYLE_ALL; int sor = 0; int sstate = 0; int dstate = 0; do { /* If we reach the right point in the source string, set the mask to * apply the desired style. Also, turn it off again afterwards. */ if (src == offset1) { sand = targetsand; sor = targetsor; } if (src == offset2) { sand = STYLE_ALL; sor = 0; } /* If we reach csoffset in the src, remember where we were in the dest * so we can move the cursor correctly. */ if (src == csoffset) cdoffset = p; } while (copy(&p, &dstate, &src, &sstate, sor, sand)); lua_pushlstring(L, dest, p - dest); lua_pushnumber(L, 1 + cdoffset - dest); return 2; } void word_init(void) { const static luaL_Reg funcs[] = { { "parseword", parseword_cb }, { "writestyled", writestyled_cb }, { "getwordtext", getwordtext_cb }, { "nextcharinword", nextcharinword_cb }, { "prevcharinword", prevcharinword_cb }, { "insertintoword", insertintoword_cb }, { "deletefromword", deletefromword_cb }, { "applystyletoword", applystyletoword_cb }, { NULL, NULL } }; lua_getglobal(L, "wg"); luaL_setfuncs(L, funcs, 0); lua_pushnumber(L, DPY_ITALIC); lua_setfield(L, -2, "ITALIC"); lua_pushnumber(L, DPY_UNDERLINE); lua_setfield(L, -2, "UNDERLINE"); lua_pushnumber(L, DPY_REVERSE); lua_setfield(L, -2, "REVERSE"); lua_pushnumber(L, DPY_BOLD); lua_setfield(L, -2, "BOLD"); lua_pushnumber(L, DPY_BRIGHT); lua_setfield(L, -2, "BRIGHT"); lua_pushnumber(L, DPY_DIM); lua_setfield(L, -2, "DIM"); } wordgrinder-0.5.1.orig/src/c/zip.c0000644000000000000000000000702412121706526013654 0ustar /* © 2013 David Given. * WordGrinder is licensed under the MIT open source license. See the COPYING * file in this distribution for the full text. */ #include "globals.h" #include #include "unzip.h" #include "zip.h" static int decompress_cb(lua_State* L) { size_t srcsize; const char* srcbuffer = luaL_checklstring(L, 1, &srcsize); int outputchunks = 0; uint8_t outputbuffer[64*1024]; z_stream zs = {0}; int i = inflateInit(&zs); if (i != Z_OK) return 0; zs.avail_in = srcsize; zs.next_in = (uint8_t*) srcbuffer; do { zs.avail_out = sizeof(outputbuffer); zs.next_out = outputbuffer; i = inflate(&zs, Z_NO_FLUSH); switch (i) { case Z_NEED_DICT: case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&zs); return 0; } int have = sizeof(outputbuffer) - zs.avail_out; lua_pushlstring(L, (char*) outputbuffer, have); outputchunks++; } while (i != Z_STREAM_END); (void)inflateEnd(&zs); lua_concat(L, outputchunks); return 1; } static int compress_cb(lua_State* L) { size_t srcsize; const char* srcbuffer = luaL_checklstring(L, 1, &srcsize); int outputchunks = 0; uint8_t outputbuffer[64*1024]; z_stream zs = {0}; int i = deflateInit(&zs, 1); if (i != Z_OK) return 0; zs.avail_in = srcsize; zs.next_in = (uint8_t*) srcbuffer; do { zs.avail_out = sizeof(outputbuffer); zs.next_out = outputbuffer; i = deflate(&zs, Z_FINISH); int have = sizeof(outputbuffer) - zs.avail_out; lua_pushlstring(L, (char*) outputbuffer, have); outputchunks++; } while (i != Z_STREAM_END); (void)deflateEnd(&zs); lua_concat(L, outputchunks); return 1; } static int readfromzip_cb(lua_State* L) { const char* zipname = luaL_checkstring(L, 1); const char* subname = luaL_checkstring(L, 2); int result = 0; unzFile zf = unzOpen(zipname); if (zf) { int i = unzLocateFile(zf, subname, 0); if (i == UNZ_OK) { unz_file_info fi; unzGetCurrentFileInfo(zf, &fi, NULL, 0, NULL, 0, NULL, 0); char* buffer = malloc(fi.uncompressed_size); if (buffer) { unzOpenCurrentFile(zf); i = unzReadCurrentFile(zf, buffer, fi.uncompressed_size); if (i == fi.uncompressed_size) { lua_pushlstring(L, buffer, fi.uncompressed_size); result = 1; } free(buffer); } } unzClose(zf); } return result; } static int writezip_cb(lua_State* L) { const char* zipname = luaL_checkstring(L, 1); luaL_checktype(L, 2, LUA_TTABLE); int result = 0; zipFile zf = zipOpen(zipname, APPEND_STATUS_CREATE); if (zf) { result = 1; lua_pushnil(L); while (lua_next(L, 2) != 0) { const char* key = lua_tostring(L, -2); size_t valuelen; const char* value = lua_tolstring(L, -1, &valuelen); int i = zipOpenNewFileInZip(zf, key, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); if (i != ZIP_OK) { result = 0; break; } i = zipWriteInFileInZip(zf, value, valuelen); if (i != ZIP_OK) { result = 0; break; } i = zipCloseFileInZip(zf); if (i != ZIP_OK) { result = 0; break; } lua_pop(L, 1); /* leave key on stack */ } zipClose(zf, NULL); } if (!result) return 0; lua_pushboolean(L, true); return 1; } void zip_init(void) { const static luaL_Reg funcs[] = { { "compress", compress_cb }, { "decompress", decompress_cb }, { "readfromzip", readfromzip_cb }, { "writezip", writezip_cb }, { NULL, NULL } }; lua_getglobal(L, "wg"); luaL_setfuncs(L, funcs, 0); } wordgrinder-0.5.1.orig/src/lua/0000755000000000000000000000000012251160512013233 5ustar wordgrinder-0.5.1.orig/src/lua/_prologue.lua0000644000000000000000000000050212121171060015722 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. -- Load the LFS module if needed (Windows has it built in). if not lfs then lfs = require "lfs" end -- Global definitions that the various source files need. Cmd = {} wordgrinder-0.5.1.orig/src/lua/addons/0000755000000000000000000000000012251160512014503 5ustar wordgrinder-0.5.1.orig/src/lua/addons/autosave.lua0000644000000000000000000001170212243442530017042 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local function announce() local settings = DocumentSet.addons.autosave if settings.enabled then NonmodalMessage("Autosave is enabled. Next save in "..settings.period.. " minute"..Pluralise(settings.period, "", "s").. ".") else NonmodalMessage("Autosave is disabled.") end end local function makefilename(pattern) local basefilename = DocumentSet.name basefilename = basefilename:gsub("%.wg$", "") basefilename = basefilename:gsub("%%", "%%%%") local timestamp = os.date("%Y-%m-%d.%H%M") timestamp = timestamp:gsub("%%", "%%%%") pattern = pattern:gsub("%%[fF]", basefilename) pattern = pattern:gsub("%%[tT]", timestamp) pattern = pattern:gsub("%%%%", "%%") return pattern end ----------------------------------------------------------------------------- -- Idle handler. This actually does the work of autosaving. do local function cb() local settings = DocumentSet.addons.autosave if not settings.enabled or not DocumentSet.changed then return end if not settings.lastsaved then settings.lastsaved = os.time() end if ((os.time() - settings.lastsaved) > (settings.period * 60)) then ImmediateMessage("Autosaving...") local filename = makefilename(settings.pattern) -- Note that autosaved documents should have autosave *dis*abled! settings.enabled = false local r, e = SaveDocumentSetRaw(filename) settings.enabled = true if not r then ModalMessage("Autosave failed", "The document could not be autosaved: "..e) else NonmodalMessage("Autosaved as "..filename) QueueRedraw() end settings.lastsaved = os.time() end end AddEventListener(Event.Idle, cb) end ----------------------------------------------------------------------------- -- Load document. Nukes the 'last autosave' field do local function cb() DocumentSet.addons.autosave.lastsaved = nil announce() end AddEventListener(Event.DocumentLoaded, cb) end ----------------------------------------------------------------------------- -- Addon registration. Create the default settings in the DocumentSet. do local function cb() DocumentSet.addons.autosave = DocumentSet.addons.autosave or { enabled = false, period = 10, pattern = "%F.autosave.%T.wg" } end AddEventListener(Event.RegisterAddons, cb) end ----------------------------------------------------------------------------- -- Configuration user interface. function Cmd.ConfigureAutosave() local settings = DocumentSet.addons.autosave if not DocumentSet.name then ModalMessage("Autosave not available", "You cannot use autosave ".. "until you have manually saved your document at least once, ".. "so that Autosave knows what base filename to use.") return false end local enabled_checkbox = Form.Checkbox { x1 = 1, y1 = 1, x2 = 33, y2 = 1, label = "Enable autosaving", value = settings.enabled } local period_textfield = Form.TextField { x1 = 33, y1 = 3, x2 = 43, y2 = 3, value = tostring(settings.period) } local example_label = Form.Label { x1 = 1, y1 = 7, x2 = -1, y2 = 7, value = "(Example filename: README.autosave.2008-08-07.1829.wg)" } local pattern_textfield = Form.TextField { x1 = 33, y1 = 5, x2 = -1, y2 = 5, value = settings.pattern, draw = function(self) self.class.draw(self) local f = Leafname(makefilename(self.value)) example_label.value = "(e.g.: .../"..f..")" example_label:draw() end } local dialogue = { title = "Configure Autosave", width = Form.Large, height = 9, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", enabled_checkbox, Form.Label { x1 = 1, y1 = 3, x2 = 32, y2 = 3, align = Form.Left, value = "Period between saves (minutes):" }, period_textfield, Form.Label { x1 = 1, y1 = 5, x2 = 32, y2 = 5, align = Form.Left, value = "Autosave filename pattern:" }, pattern_textfield, example_label, } while true do local result = Form.Run(dialogue, RedrawScreen, "SPACE to toggle, RETURN to confirm, CTRL+C to cancel") if not result then return false end local enabled = enabled_checkbox.value local period = tonumber(period_textfield.value) local pattern = pattern_textfield.value if not period then ModalMessage("Parameter error", "The period field must be a valid number.") elseif (pattern:len() == 0) then ModalMessage("Parameter error", "The filename pattern cannot be empty.") elseif pattern:find("%%[^%%ftFT]") then ModalMessage("Parameter error", "The filename pattern can only contain ".. "%%, %F or %T fields.") else settings.enabled = enabled settings.period = period settings.pattern = pattern settings.lastsaved = nil DocumentSet:touch() announce() return true end end return false end wordgrinder-0.5.1.orig/src/lua/addons/docsetman.lua0000644000000000000000000000715212237030623017173 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. function Cmd.AddBlankDocument(name) if not name then name = PromptForString("Name of new document?", "Please enter the new document name:") if not name or (name == "") then return false end end if DocumentSet.documents[name] then ModalMessage("Name in use", "Sorry! There's already a document with that name in this document set.") return false end DocumentSet:addDocument(CreateDocument(), name) DocumentSet:setCurrent(name) QueueRedraw() return true end function Cmd.ManageDocumentsUI() local browser = Form.Browser { focusable = true, type = Form.Browser, x1 = 1, y1 = 2, x2 = -1, y2 = -4, changed = function(self) Cmd.ChangeDocument(self.data[self.cursor].document.name) return "redraw" end } local dialogue = { title = "Document Manager", width = Form.Large, height = Form.Large, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "cancel", ["KEY_ENTER"] = "cancel", ["u"] = function() if (browser.cursor > 1) then local document = browser.data[browser.cursor].document DocumentSet:moveDocumentIndexTo(document.name, browser.cursor - 1) browser.cursor = browser.cursor - 1 return "confirm" end return "nop" end, ["d"] = function() if (browser.cursor < #browser.data) then DocumentSet:moveDocumentIndexTo(Document.name, browser.cursor + 1) browser.cursor = browser.cursor + 1 return "confirm" end return "nop" end, ["r"] = function() local name = PromptForString("Change name of current document", "Please enter the new document name:", Document.name) if not name or (name == Document.name) then return "confirm" end if not DocumentSet:renameDocument(Document.name, name) then ModalMessage("Name in use", "Sorry! There's already a document with that name in this document set.") return "confirm" end return "confirm" end, ["x"] = function() if (#browser.data == 1) then ModalMessage("Unable to delete document", "You can't delete the last document from the document set.") return "confirm" end if not PromptForYesNo("Delete this document?", "Are you sure you want to delete the document '" .. Document.name .."'? It will be removed from the current document set, and will be gone forever.") then return false end if not DocumentSet:deleteDocument(Document.name) then ModalMessage("Unable to delete document", "You can't delete that document.") return "confirm" end return "confirm" end, ["n"] = function() Cmd.AddBlankDocument() return "confirm" end, Form.Label { x1 = 1, y1 = 1, x2 = -1, y2 = 1, value = "Select heading to jump to:" }, Form.Label { x1 = 1, y1 = -3, x2 = -1, y2 = -3, value = "U: Move document up R: Rename document" }, Form.Label { x1 = 1, y1 = -2, x2 = -1, y2 = -2, value = "D: Move document down X: Delete document" }, Form.Label { x1 = 1, y1 = -1, x2 = -1, y2 = -1, value = "N: Create blank document RETURN, ^C: Close dialogue" }, browser, } while true do local data = {} local current = nil for dn, d in ipairs(DocumentSet:getDocumentList()) do data[dn] = { document = d, label = d.name or "(unnamed)" } if (d == Document) then current = dn end end browser.data = data browser.cursor = current local result = Form.Run(dialogue, RedrawScreen) QueueRedraw() if not result then return true end end end wordgrinder-0.5.1.orig/src/lua/addons/goto.lua0000644000000000000000000000354012243442530016164 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local string_rep = string.rep local table_concat = table.concat local function gotobrowser(data, index) local browser = Form.Browser { focusable = true, type = Form.Browser, x1 = 1, y1 = 2, x2 = -1, y2 = -1, data = data, cursor = index } local dialogue = { title = "Table of Contents", width = Form.Large, height = Form.Large, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", Form.Label { x1 = 1, y1 = 1, x2 = -1, y2 = 1, value = "Select heading to jump to:" }, browser, } local result = Form.Run(dialogue, RedrawScreen, "RETURN to select item, CTRL+C to cancel") QueueRedraw() if result then return browser.cursor else return nil end end function Cmd.Goto() ImmediateMessage("Scanning document...") local data = {} local levelcount = {0, 0, 0, 0} local currentheading = 1 for paran, para in ipairs(Document) do local _, _, level = para.style.name:find("^H(%d)$") if level then level = tonumber(level) levelcount[level] = levelcount[level] + 1 local s = {} for i = 1, level do s[#s+1] = levelcount[i] .. "." end s[#s+1] = " " s[#s+1] = para:asString() data[#data+1] = { label = table_concat(s), paran = paran } if (paran <= Document.cp) then currentheading = #data end end end if (#data == 0) then ModalMessage("No contents available", "You must have some heading paragraphs in your document to use the table of contents.") return false end local result = gotobrowser(data, currentheading) QueueRedraw() if result then Document.cp = data[result].paran Document.cw = 1 Document.co = 1 return true end return false end wordgrinder-0.5.1.orig/src/lua/addons/pagecount.lua0000644000000000000000000000444612243442530017207 0ustar -- © 2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. ----------------------------------------------------------------------------- -- Build the status bar. do local function cb(event, token, terms) local settings = DocumentSet.addons.pagecount or {} if settings.enabled then local pages = math.floor((Document.wordcount or 0) / settings.wordsperpage) terms[#terms+1] = string.format("%d %s", pages, Pluralise(pages, "page", "pages")) end end AddEventListener(Event.BuildStatusBar, cb) end ----------------------------------------------------------------------------- -- Addon registration. Create the default settings in the DocumentSet. do local function cb() DocumentSet.addons.pagecount = DocumentSet.addons.pagecount or { enabled = false, wordsperpage = 250, } end AddEventListener(Event.RegisterAddons, cb) end ----------------------------------------------------------------------------- -- Configuration user interface. function Cmd.ConfigurePageCount() local settings = DocumentSet.addons.pagecount local enabled_checkbox = Form.Checkbox { x1 = 1, y1 = 1, x2 = 33, y2 = 1, label = "Show approximate page count", value = settings.enabled } local count_textfield = Form.TextField { x1 = 33, y1 = 3, x2 = 43, y2 = 3, value = tostring(settings.wordsperpage) } local dialogue = { title = "Configure Page Count", width = Form.Large, height = 5, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", enabled_checkbox, Form.Label { x1 = 1, y1 = 3, x2 = 32, y2 = 3, align = Form.Left, value = "Number of words per page:" }, count_textfield, } while true do local result = Form.Run(dialogue, RedrawScreen, "SPACE to toggle, RETURN to confirm, CTRL+C to cancel") if not result then return false end local enabled = enabled_checkbox.value local wordsperpage = tonumber(count_textfield.value) if not wordsperpage then ModalMessage("Parameter error", "The number of words per page must be a valid number.") else settings.enabled = enabled settings.wordsperpage = wordsperpage DocumentSet:touch() return true end end return false end wordgrinder-0.5.1.orig/src/lua/addons/scrapbook.lua0000644000000000000000000001051612243442530017200 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local function maketimestamp(pattern, name) name = name or Document.name name = name:gsub("%%", "%%%%") local timestamp = os.date("%Y-%m-%d.%H%M") timestamp = timestamp:gsub("%%", "%%%%") pattern = pattern:gsub("%%[nN]", name) pattern = pattern:gsub("%%[tT]", timestamp) pattern = pattern:gsub("%%%%", "%%") return pattern end function Cmd.CutToScrapbook() if not Document.mp then NonmodalMessage("There's nothing selected.") return false end if not Cmd.Cut() then return false end return Cmd.PasteToScrapbook() end function Cmd.CopyToScrapbook() if not Document.mp then NonmodalMessage("There's nothing selected.") return false end if not Cmd.Copy() then return false end return Cmd.PasteToScrapbook() end function Cmd.PasteToScrapbook() local buffer = DocumentSet:getClipboard() if not buffer then NonmodalMessage("There's nothing on the clipboard.") return false end local settings = DocumentSet.addons.scrapbook local currentdocument = Document.name if not DocumentSet:findDocument(settings.document) then DocumentSet:addDocument(CreateDocument(), settings.document) NonmodalMessage("Creating scrapbook in document '"..settings.document.."'.") end DocumentSet:setCurrent(settings.document) Cmd.GotoEndOfDocument() Cmd.UnsetMark() Cmd.SplitCurrentParagraph() if settings.timestamp then Cmd.InsertStringIntoParagraph(maketimestamp(settings.pattern, currentdocument)) Cmd.ChangeParagraphStyle("H1") Cmd.SplitCurrentParagraph() end Cmd.ChangeParagraphStyle("P") Cmd.Paste() DocumentSet:setCurrent(currentdocument) NonmodalMessage("Fragment added to scrapbook.") return false end ----------------------------------------------------------------------------- -- Addon registration. Create the default settings in the DocumentSet. do local function cb() DocumentSet.addons.scrapbook = DocumentSet.addons.scrapbook or { document = "Scrapbook", timestamp = true, pattern = "Item from '%N' at %T:" } end AddEventListener(Event.RegisterAddons, cb) end ----------------------------------------------------------------------------- -- Configuration user interface. function Cmd.ConfigureScrapbook() local settings = DocumentSet.addons.scrapbook local document_textfield = Form.TextField { x1 = 33, y1 = 1, x2 = -1, y2 = 1, value = tostring(settings.document) } local timestamp_checkbox = Form.Checkbox { x1 = 1, y1 = 3, x2 = 33, y2 = 3, label = "Enable timestamp", value = settings.timestamp } local example_label = Form.Label { x1 = 1, y1 = 7, x2 = -1, y2 = 7, } local pattern_textfield = Form.TextField { x1 = 33, y1 = 5, x2 = -1, y2 = 5, value = settings.pattern, draw = function(self) self.class.draw(self) example_label.value = "(Example timestamp: "..maketimestamp(self.value)..")" example_label:draw() end } local dialogue = { title = "Configure Timestamp", width = Form.Large, height = 9, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", Form.Label { x1 = 1, y1 = 1, x2 = 32, y2 = 1, align = Form.Left, value = "Name of scrapbook document:" }, document_textfield, timestamp_checkbox, Form.Label { x1 = 1, y1 = 5, x2 = 32, y2 = 5, align = Form.Left, value = "Timestamp pattern:" }, pattern_textfield, example_label, } while true do local result = Form.Run(dialogue, RedrawScreen, "SPACE to toggle, RETURN to confirm, CTRL+C to cancel") if not result then return false end local document = document_textfield.value local timestamp = timestamp_checkbox.value local pattern = pattern_textfield.value if (document:len() == 0) then ModalMessage("Parameter error", "The document name cannot be empty.") elseif (pattern:len() == 0) then ModalMessage("Parameter error", "The timestamp pattern cannot be empty.") elseif pattern:find("%%[^%%ntNT]") then ModalMessage("Parameter error", "The filename pattern can only contain ".. "%%, %N or %T fields.") else settings.document = document settings.timestamp = timestamp settings.pattern = pattern DocumentSet:touch() return true end end return false end wordgrinder-0.5.1.orig/src/lua/addons/widescreen.lua0000644000000000000000000000432112245745434017355 0ustar -- © 2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. ----------------------------------------------------------------------------- -- Fetch the maximum allowed width. function GetMaximumAllowedWidth(screenwidth) local addons = DocumentSet.addons or {} local settings = addons.widescreen if not settings or not settings.enabled then return screenwidth end return math.min(screenwidth, settings.maxwidth) end ----------------------------------------------------------------------------- -- Addon registration. Create the default settings in the DocumentSet. do local function cb() DocumentSet.addons.widescreen = DocumentSet.addons.widescreen or { enabled = false, maxwidth = 80 } end AddEventListener(Event.RegisterAddons, cb) end ----------------------------------------------------------------------------- -- Configuration user interface. function Cmd.ConfigureWidescreen() local settings = DocumentSet.addons.widescreen local enabled_checkbox = Form.Checkbox { x1 = 1, y1 = 1, x2 = 33, y2 = 1, label = "Enable widescreen mode", value = settings.enabled } local maxwidth_textfield = Form.TextField { x1 = 33, y1 = 3, x2 = 43, y2 = 3, value = tostring(settings.maxwidth) } local dialogue = { title = "Configure Widescreen Mode", width = Form.Large, height = 5, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", enabled_checkbox, Form.Label { x1 = 1, y1 = 3, x2 = 32, y2 = 3, align = Form.Left, value = "Maximum allowed width:", }, maxwidth_textfield, } while true do local result = Form.Run(dialogue, RedrawScreen, "SPACE to toggle, RETURN to confirm, CTRL+C to cancel") if not result then return false end local enabled = enabled_checkbox.value local maxwidth = tonumber(maxwidth_textfield.value) if not maxwidth or (maxwidth < 20) then ModalMessage("Parameter error", "The maximum width must be a valid number that's at least 20.") else settings.enabled = enabled settings.maxwidth = maxwidth DocumentSet:touch() return true end end return false end wordgrinder-0.5.1.orig/src/lua/browser.lua0000644000000000000000000000762712245677273015461 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local min = min local max = max local int = math.floor local string_rep = string.rep local Write = wg.write local GotoXY = wg.gotoxy local SetNormal = wg.setnormal local SetBold = wg.setbold local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local SetDim = wg.setdim local GetStringWidth = wg.getstringwidth local GetBytesOfCharacter = wg.getbytesofcharacter function FileBrowser(title, message, saving, default) local files = {} for i in lfs.dir(".") do if (i ~= ".") and ((i == "..") or not i:match("^%.")) then local attr = lfs.attributes(i) if attr then attr.name = i files[#files+1] = attr end end end table.sort(files, function(a, b) if (a.mode == b.mode) then return a.name < b.name end if (a.mode == "directory") then return true end return false end) local labels = {} local defaultn = 1 for _, attr in ipairs(files) do local dmarker = " " if (attr.mode == "directory") then dmarker = "◇ " end labels[#labels+1] = { data = attr.name, label = dmarker..attr.name } if (attr.name == default) then defaultn = #labels end end -- Windows will sometimes give you a directory with no entries -- in it at all (e.g. Documents and Settings on Win7). This is -- annoying. if (#labels == 0) then labels[#labels+1] = { data = "..", label = "◇ .." } end local f = Browser(title, lfs.currentdir(), message, labels, default, defaultn) if not f then return nil end if (ARCH == "windows") and f:match("^%a:$") then -- The user has typed a drive specifier; turn it into a path. f = f.."/" end local attr, e = lfs.attributes(f) if not saving and e then ModalMessage("File inaccessible", "The file '"..f.."' could not be accessed: "..e) return FileBrowser(title, message, saving) end if attr and (attr.mode == "directory") then lfs.chdir(f) return FileBrowser(title, message, saving) end if saving and not e then local r = PromptForYesNo("Overwrite file?", "The file '"..f.."' already exists. Do you want to overwrite it?") if (r == nil) then return nil elseif r then return lfs.currentdir().."/"..f else return FileBrowser(title, message, saving) end end return lfs.currentdir().."/"..f end function Browser(title, topmessage, bottommessage, data, default, defaultn) local browser = Form.Browser { focusable = false, type = Form.Browser, x1 = 1, y1 = 2, x2 = -1, y2 = -5, data = data, cursor = defaultn or 1 } local textfield = Form.TextField { x1 = GetStringWidth(bottommessage) + 3, y1 = -3, x2 = -1, y2 = -2, value = default or data[1].data, } local function navigate(self, key) local action = browser[key](browser) textfield.value = data[browser.cursor].data textfield.cursor = textfield.value:len() + 1 textfield.offset = 1 textfield:draw() return action end local helptext if (ARCH == "windows") then helptext = "enter an absolute path or drive letter ('c:') to go there" else helptext = "enter an absolute path to go there" end local dialogue = { title = title, width = Form.Large, height = Form.Large, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", ["KEY_UP"] = navigate, ["KEY_DOWN"] = navigate, ["KEY_NPAGE"] = navigate, ["KEY_PPAGE"] = navigate, Form.Label { x1 = 1, y1 = 1, x2 = -1, y2 = 1, value = topmessage }, textfield, browser, Form.Label { x1 = 1, y1 = -3, x2 = GetStringWidth(bottommessage) + 1, y2 = -3, value = bottommessage }, Form.Label { x1 = 1, y1 = -1, x2 = -1, y2 = -1, value = helptext } } local result = Form.Run(dialogue, RedrawScreen, "RETURN to confirm, CTRL+C to cancel") QueueRedraw() if result then return textfield.value else return nil end end wordgrinder-0.5.1.orig/src/lua/cli.lua0000644000000000000000000000547212123634064014523 0ustar -- © 2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local string_find = string.find local table_concat = table.concat local stderr = io.stderr local import_table = { ["wg"] = Cmd.LoadDocumentSet, ["odt"] = Cmd.ImportODTFile, ["html"] = Cmd.ImportHTMLFile, ["txt"] = Cmd.ImportTextFile } local export_table = { ["wg"] = Cmd.SaveCurrentDocumentAs, ["odt"] = Cmd.ExportODTFile, ["html"] = Cmd.ExportHTMLFile, ["tr"] = Cmd.ExportTroffFile, ["tex"] = Cmd.ExportLatexFile, ["txt"] = Cmd.ExportTextFile, } local function message(...) stderr:write("wordgrinder: ", ...) stderr:write("\n") stderr:flush() end local function usererror(...) message(...) os.exit(1) end --- Engages CLI mode. function EngageCLI() function ImmediateMessage(s) if s then message(s) end end function ModalMessage(s1, s2) if s2 then message(s2) end end end --- Converts between two files. -- -- @param file1 Source filename -- @param file2 Destination filename function CliConvert(file1, file2) EngageCLI() local function decode_filename(f) local _, _, root, extension, hassubdoc, subdoc = string_find(f, "^(.*)%.(%w*)(:?)(.*)$") if not root or not extension then usererror("unable to parse filename '", f, "'") end return root, extension, hassubdoc, subdoc end local f1r, f1e, f1hs, f1s = decode_filename(file1) local f1 = f1r.."."..f1e local f2r, f2e, f2hs, f2s = decode_filename(file2) local f2 = f2r.."."..f2e if (f2hs ~= "") then usererror("you cannot specify a document name for the output file") end local function supported_extensions(t) local s = {} for k, v in pairs(t) do s[#s+1] = k end return table_concat(s, " ") end local importer = import_table[f1e] if not importer then usererror("don't know how to import extension '", f1e, "' ", "(supported extensions are: ", supported_extensions(import_table), ")") end local exporter = export_table[f2e] if not exporter then usererror("don't know how to export extension '", f2e, "' ", "(supported extensions are: ", supported_extensions(export_table), ")") end if not importer(f1) then usererror("failed") end if (f1hs ~= "") then if (f1e == "wg") then -- If the user specified a document name, and we loaded a wg file, -- then select the specified document. local dl = DocumentSet:getDocumentList() if not dl[f1s] then usererror("no such document '", f1s, "'") end DocumentSet:setCurrent(f1s) else -- Otherwise, rename the document we just imported to the name -- that the user specified. local name = Document.name DocumentSet:renameDocument(name, f1s) end end if not exporter(f2) then usererror("failed") end os.exit(0) end wordgrinder-0.5.1.orig/src/lua/document.lua0000644000000000000000000002777312243254677015615 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local table_remove = table.remove local table_insert = table.insert local table_concat = table.concat local Write = wg.write local WriteStyled = wg.writestyled local ClearToEOL = wg.cleartoeol local SetNormal = wg.setnormal local SetBold = wg.setbold local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local SetDim = wg.setdim local GetStringWidth = wg.getstringwidth local GetBytesOfCharacter = wg.getbytesofcharacter local GetWordText = wg.getwordtext local BOLD = wg.BOLD local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local REVERSE = wg.REVERSE local BRIGHT = wg.BRIGHT local DIM = wg.DIM local stylemarkup = { ["H1"] = ITALIC + BRIGHT + BOLD + UNDERLINE, ["H2"] = BRIGHT + BOLD + UNDERLINE, ["H3"] = ITALIC + BRIGHT + BOLD, ["H4"] = BRIGHT + BOLD } DocumentSetClass = { -- remove any cached data prior to saving purge = function(self) for _, l in ipairs(self.documents) do l:purge() end end, touch = function(self) self.changed = true self.justchanged = true end, clean = function(self) self.changed = nil self.justchanged = nil end, getDocumentList = function(self) return self.documents end, _findDocument = function(self, name) for i, d in ipairs(self.documents) do if (d.name == name) then return i end end return nil end, findDocument = function(self, name) local document = self.documents[name] if not document then document = self.documents[self:_findDocument(name)] if document then ModalMessage("Document index inconsistency corrected", "Something freaky happened to '"..name.."'.") self.documents[name] = document end end return document end, addDocument = function(self, document, name, index) document.name = name local n = self:_findDocument(name) or (#self.documents + 1) self.documents[n] = document self.documents[name] = document if not self.current or (self.current.name == name) then self:setCurrent(name) end self:touch() RebuildDocumentsMenu(self.documents) end, moveDocumentIndexTo = function(self, name, targetIndex) local n = self:_findDocument(name) if not n then return end local document = self.documents[n] table_remove(self.documents, n) table_insert(self.documents, targetIndex, document) self:touch() RebuildDocumentsMenu(self.documents) end, deleteDocument = function(self, name) if (#self.documents == 1) then return false end local n = self:_findDocument(name) if not n then return end local document = self.documents[n] table_remove(self.documents, n) self.documents[name] = nil self:touch() RebuildDocumentsMenu(self.documents) if (Document == document) then document = self.documents[n] if not document then document = self.documents[#self.documents] end self:setCurrent(document.name) end return true end, setCurrent = function(self, name) -- Ensure any housekeeping on the current document gets done. if Document and Document.changed then FireEvent(Event.Changed) end Document = self.documents[name] if not Document then Document = self.documents[1] end self.current = Document ResizeScreen() end, renameDocument = function(self, oldname, newname) if self.documents[newname] then return false end local d = self.documents[oldname] self.documents[oldname] = nil self.documents[newname] = d d.name = newname self:touch() RebuildDocumentsMenu(self.documents) return true end, setClipboard = function(self, clipboard) self.clipboard = clipboard end, getClipboard = function(self) return self.clipboard end, } DocumentClass = { appendParagraph = function(self, p) self[#self+1] = p end, insertParagraphBefore = function(self, paragraph, pn) table.insert(self, pn, paragraph) end, deleteParagraphAt = function(self, pn) table.remove(self, pn) end, wrap = function(self, width) self.wrapwidth = width end, getMarks = function(self) if not self.mp then return end local mp1 = self.mp local mw1 = self.mw local mo1 = self.mo local mp2 = self.cp local mw2 = self.cw local mo2 = self.co if (mp1 > mp2) or ((mp1 == mp2) and ((mw1 > mw2) or ((mw1 == mw2) and (mo1 > mo2))) ) then return mp2, mw2, mo2, mp1, mw1, mo1 end return mp1, mw1, mo1, mp2, mw2, mo2 end, -- remove any cached data prior to saving purge = function(self) for _, l in ipairs(self) do l:touch() end self.topp = nil self.topw = nil self.botp = nil self.botw = nil self.wrapwidth = nil end, -- calculate space above this paragraph spaceAbove = function(self, pn) local paragraph = self[pn] local paragraphabove = self[pn - 1] local sa = paragraph.style.above or 0 -- FIXME local sb = 0 if paragraphabove then sb = paragraphabove.style.below or 0 -- FIXME end if (sa > sb) then return sa else return sb end end, -- calculate space below this paragraph spaceBelow = function(self, pn) local paragraph = self[pn] local paragraphbelow = self[pn + 1] local sb = paragraph.style.below or 0 -- FIXME local sa = 0 if paragraphbelow then sa = paragraphbelow.style.above or 0 -- FIXME end if (sa > sb) then return sa else return sb end end, } ParagraphClass = { copy = function(self) local words = {} for _, w in ipairs(self) do words[#words+1] = w:copy() end return CreateParagraph(self.style, words) end, touch = function(self) self.lines = nil self.wrapwidth = nil end, wrap = function(self, width) width = width or Document.wrapwidth width = width - (self.style.indent or 0) if (self.wrapwidth ~= width) then local lines = {} local line = {wn = 1} local w = 0 for wn, word in ipairs(self) do word.x = w local ww = word:getWidth() w = w + ww if (w >= width) then lines[#lines+1] = line line = {wn = wn} w = ww word.x = 0 end line[#line+1] = word end if (#line > 0) then lines[#lines+1] = line end self.wrapwidth = width self.lines = lines end return self.lines end, renderLine = function(self, line, x, y) width = width or (ScreenWidth - x) local cstyle = stylemarkup[self.style.name] or 0 local ostyle = 0 for wn, w in ipairs(line) do local text = w.text ostyle = WriteStyled(x+w.x, y, text, ostyle, nil, nil, cstyle) end end, renderMarkedLine = function(self, line, x, y, width, pn) width = width or (ScreenWidth - x) marked = marked or false local lwn = line.wn local mp1, mw1, mo1, mp2, mw2, mo2 = Document:getMarks() local cstyle = stylemarkup[self.style.name] or 0 local ostyle = 0 for wn, w in ipairs(line) do local s, e wn = lwn + wn - 1 if (pn < mp1) or (pn > mp2) then s = nil elseif (pn > mp1) and (pn < mp2) then s = 1 else if (pn == mp1) and (pn == mp2) then if (wn == mw1) and (wn == mw2) then s = mo1 e = mo2 elseif (wn == mw1) then s = mo1 elseif (wn == mw2) then s = 1 e = mo2 elseif (wn > mw1) and (wn < mw2) then s = 1 end elseif (pn == mp1) then if (wn > mw1) then s = 1 elseif (wn == mw1) then s = mo1 end else s = 1 if (wn > mw2) then s = nil elseif (wn == mw2) then e = mo2 end end end ostyle = WriteStyled(x+w.x, y, w.text, ostyle, s, e, cstyle) end end, -- returns: line number, word number in line getLineOfWord = function(self, wn) local lines = self:wrap() for ln, l in ipairs(lines) do if (wn <= #l) then return ln, wn end wn = wn - #l end return nil, nil end, -- returns: word number getWordOfLine = function(self, ln) local lines = self:wrap() return lines[ln].wn end, -- returns: X offset, line number, word number in line getXOffsetOfWord = function(self, wn) local lines = self:wrap() local x = self[wn].x local ln, wn = self:getLineOfWord(wn) return x, ln, wn end, deleteWordAt = function(self, pos) table.remove(self, pos) self:touch() end, insertWordBefore = function(self, pos, word) table.insert(self, pos, word) self:touch() end, appendWord = function(self, word) self[#self+1] = word self:touch() end, appendWords = function(self, words) for _, w in ipairs(words) do self:appendWord(w) end self:touch() end, split = function(self, wn) local p1 = CreateParagraph(self.style) local p2 = CreateParagraph(self.style) for i=1, wn-1 do p1:appendWord(self[i]) end for i=wn, #self do p2:appendWord(self[i]) end return p1, p2 end, truncateAtWord = function(self, wn) while (self[wn]) do table.remove(self, wn) end self:touch() end, changeStyle = function(self, style) self.style = style self:touch() end, -- return an unstyled string containing the contents of the paragraph. asString = function(self) local s = {} for _, w in ipairs(self) do s[#s+1] = w:asString() end return table_concat(s, " ") end } WordClass = { copy = function(self) return CreateWord(self.text) end, getWidth = function(self) return GetStringWidth(self.text) + 1 end, -- converts byte offset to X position getXOffsetOfChar = function(self, o) return GetStringWidth(self.text:sub(1, o-1)) end, -- converts X position to byte offset getByteOfChar = function(self, x) local text = self.text local len = text:len() local o = 1 while (o <= len) do local charlen = GetBytesOfCharacter(string.byte(text, o)) local char = text:sub(o, o+charlen-1) local ww = GetStringWidth(char) if (ww > x) then return o end x = x - ww o = o + charlen end return len + 1 end, -- returns an unstyled string containing the word contents asString = function(self) return GetWordText(self.text) end, } local function create_styles() local styles = { { desc = "Plain text", name = "P", html = "P", above = 1, below = 1, }, { desc = "Heading #1", name = "H1", html = "H1", above = 3, below = 1, }, { desc = "Heading #2", name = "H2", html = "H2", above = 2, below = 1, }, { desc = "Heading #3", name = "H3", html = "H3", above = 1, below = 1, }, { desc = "Heading #4", name = "H4", html = "H4", above = 1, below = 1, }, { desc = "Indented text", name = "Q", html = "BLOCKQUOTE", indent = 4, above = 1, below = 1, }, { desc = "List item with bullet", name = "LB", html = "LI", above = 1, below = 1, indent = 4, bullet = "-", }, { desc = "List item without bullet", name = "L", html = "LI", above = 1, below = 1, indent = 4, }, { desc = "Indented text, run together", name = "V", html = "BLOCKQUOTE", indent = 4, above = 0, below = 0 }, { desc = "Preformatted text", name = "PRE", html = "PRE", indent = 4, above = 0, below = 0 }, { desc = "Raw data exported to output file", name = "RAW", html = nil, indent = 0, above = 0, below = 0 } } for _, s in ipairs(styles) do styles[s.name] = s end return styles end function CreateDocumentSet() local ds = { fileformat = FILEFORMAT, statusbar = true, idletime = 3, documents = {}, styles = create_styles(), addons = {}, } setmetatable(ds, {__index = DocumentSetClass}) return ds end function CreateDocument() local d = { wrapwidth = nil, viewmode = 1, margin = 0, cp = 1, cw = 1, co = 1, } setmetatable(d, {__index = DocumentClass}) local p = CreateParagraph(DocumentSet.styles["P"], {CreateWord()}) d:appendParagraph(p) return d end function CreateParagraph(style, words) words = words or {} words.style = style or DocumentSet.styles["P"] setmetatable(words, {__index = ParagraphClass}) return words end function CreateWord(text) local w = { text = text or "" } setmetatable(w, {__index = WordClass}) return w end wordgrinder-0.5.1.orig/src/lua/events.lua0000644000000000000000000000443012243251035015245 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local listeners = {} Event = {} Event.Redraw = {} --- the screen has just been redrawn Event.Idle = {} --- the user isn't touching the keyboard Event.WaitingForUser = {} --- we're about to wait for a keypress Event.Changed = {} --- the document's been changed Event.DocumentCreated = {} --- a new documentset has just been created Event.DocumentLoaded = {} --- a new documentset has just been loaded Event.DocumentUpgrade = {} --- (oldversion, newversion) the documentset is being upgraded Event.RegisterAddons = {} --- all addons should register themselves in the documentset Event.BuildStatusBar = {} --- (statusbararray) the contents of the statusbar is being calculated --- Adds a listener for a particular event. -- The supplied callback is added as a listener for the specified event. -- The order in which listeners are called is not defined. A callback may -- be registered for any number of events. -- -- The function returns a callback token which is unique for every listener; -- it can be used to unregister the listener. -- -- @param event the event to register for -- @param callback the callback to register -- @return the callback token function AddEventListener(event, callback) -- Ensure there's a listener table for this event. if not listeners[event] then listeners[event] = {} end -- Register the callback. local token = {event} listeners[event][token] = callback return token end --- Removes a listener for a particular event. -- The listener referred to by the handle is removed from the specified -- event. -- -- @param token a token returned by AddEventListener function RemoveEventListener(token) local event = token[1] listeners[event][token] = nil end --- Fires an event. -- Any callbacks registered for the event will be called (in any order). -- -- @param event the event to fire -- @param ... any additional event parameters function FireEvent(event, ...) local l = listeners[event] if not l then return end for token, callback in pairs(l) do callback(event, token, ...) end end wordgrinder-0.5.1.orig/src/lua/export/0000755000000000000000000000000012251160512014554 5ustar wordgrinder-0.5.1.orig/src/lua/export/html.lua0000644000000000000000000001316612243251035016234 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. ----------------------------------------------------------------------------- -- The exporter itself. local function unhtml(s) s = s:gsub("&", "&") s = s:gsub("<", "<") s = s:gsub(">", ">") return s end local style_tab = { ["H1"] = {false, '

', '

'}, ["H2"] = {false, '

', '

'}, ["H3"] = {false, '

', '

'}, ["H4"] = {false, '

', '

'}, ["P"] = {false, '

', '

'}, ["L"] = {false, '
  • ', '
  • '}, ["LB"] = {false, '
  • ', '
  • '}, ["Q"] = {false, '
    ', '
    '}, ["V"] = {false, '
    ', '
    '}, ["RAW"] = {false, '', ''}, ["PRE"] = {true, '
    ', '
    '} } local function callback(writer, document) local settings = DocumentSet.addons.htmlexport local currentpara = nil function changepara(newpara) local currentstyle = style_tab[currentpara] local newstyle = style_tab[newpara] if (newpara ~= currentpara) or not newpara or not currentstyle[1] or not newstyle[1] then if currentstyle then writer(currentstyle[3]) end writer("\n") if newstyle then writer(newstyle[2]) end currentpara = newpara else writer("\n") end end return ExportFileUsingCallbacks(document, { prologue = function() writer('\n') writer('\n') writer('\n') writer('\n') writer('', unhtml(document.name), '\n') writer('\n') end, rawtext = function(s) writer(s) end, text = function(s) writer(unhtml(s)) end, notext = function(s) writer('
    ') end, italic_on = function() writer(settings.italic_on) end, italic_off = function() writer(settings.italic_off) end, underline_on = function() writer(settings.underline_on) end, underline_off = function() writer(settings.underline_off) end, bold_on = function() writer(settings.bold_on) end, bold_off = function() writer(settings.bold_off) end, list_start = function() writer('
      ') end, list_end = function() writer('
    ') end, paragraph_start = function(style) changepara(style) end, paragraph_end = function(style) end, epilogue = function() changepara(nil) writer('\n') writer('\n') end }) end function Cmd.ExportHTMLFile(filename) return ExportFileWithUI(filename, "Export HTML File", ".html", callback) end ----------------------------------------------------------------------------- -- Addon registration. Set the HTML export settings. do local function cb() DocumentSet.addons.htmlexport = DocumentSet.addons.htmlexport or { underline_on = "", underline_off = "", italic_on = "", italic_off = "" } local s = DocumentSet.addons.htmlexport s.bold_on = s.bold_on or "" s.bold_off = s.bold_off or "" end AddEventListener(Event.RegisterAddons, cb) end ----------------------------------------------------------------------------- -- Configuration user interface. function Cmd.ConfigureHTMLExport() local settings = DocumentSet.addons.htmlexport local underline_on_textfield = Form.TextField { x1 = 16, y1 = 1, x2 = -1, y2 = 1, value = settings.underline_on } local underline_off_textfield = Form.TextField { x1 = 16, y1 = 3, x2 = -1, y2 = 3, value = settings.underline_off } local italic_on_textfield = Form.TextField { x1 = 16, y1 = 5, x2 = -1, y2 = 5, value = settings.italic_on } local italic_off_textfield = Form.TextField { x1 = 16, y1 = 7, x2 = -1, y2 = 7, value = settings.italic_off } local bold_on_textfield = Form.TextField { x1 = 16, y1 = 9, x2 = -1, y2 = 9, value = settings.bold_on } local bold_off_textfield = Form.TextField { x1 = 16, y1 = 11, x2 = -1, y2 = 11, value = settings.bold_off } local dialogue = { title = "Configure HTML Export", width = Form.Large, height = 13, stretchy = false, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", Form.Label { x1 = 1, y1 = 1, x2 = 32, y2 = 1, align = Form.Left, value = "Underline on:" }, underline_on_textfield, Form.Label { x1 = 1, y1 = 3, x2 = 32, y2 = 3, align = Form.Left, value = "Underline off:" }, underline_off_textfield, Form.Label { x1 = 1, y1 = 5, x2 = 32, y2 = 5, align = Form.Left, value = "Italics on:" }, italic_on_textfield, Form.Label { x1 = 1, y1 = 7, x2 = 32, y2 = 7, align = Form.Left, value = "Italics off:" }, italic_off_textfield, Form.Label { x1 = 1, y1 = 9, x2 = 32, y2 = 9, align = Form.Left, value = "Bold on:" }, bold_on_textfield, Form.Label { x1 = 1, y1 = 11, x2 = 32, y2 = 11, align = Form.Left, value = "Bold off:" }, bold_off_textfield, } while true do local result = Form.Run(dialogue, RedrawScreen, "SPACE to toggle, RETURN to confirm, CTRL+C to cancel") if not result then return false end settings.underline_on = underline_on_textfield.value settings.underline_off = underline_off_textfield.value settings.italic_on = italic_on_textfield.value settings.italic_off = italic_off_textfield.value settings.bold_on = bold_on_textfield.value settings.bold_off = bold_off_textfield.value return true end return false end wordgrinder-0.5.1.orig/src/lua/export/latex.lua0000644000000000000000000000467412243251035016411 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local untextab = { ["#"] = "\\#", ["$"] = "\\$", ["&"] = "\\&", ["{"] = "\\{", ["}"] = "\\}", ["_"] = "\\_{}", ["^"] = "\\^{}", ["~"] = "\\~{}", ["%"] = "\\%", ["<"] = "$\\langle$", [">"] = "$\\rangle$", ["\\"] = "$\\backslash$" } local function untex(s) s = s:gsub("[#$&{}\\_^~%%<>]", untextab) return s end local style_tab = { ["H1"] = {'\\section{', '}'}, ["H2"] = {'\\subsection{', '}'}, ["H3"] = {'\\subsubsection{', '}'}, ["H4"] = {'\\paragraph{', '}'}, ["P"] = {'', '\n'}, ["L"] = {'\\item[]{', '}'}, ["LB"] = {'\\item{', '}'}, ["Q"] = {'\\begin{quotation}\n', '\n\\end{quotation}'}, ["V"] = {'\\begin{quotation}\n', '\n\\end{quotation}'}, ["RAW"] = {'', ''}, ["PRE"] = {'\\begin{verbatim}\n', '\n\\end{verbatim}'} } local function callback(writer, document) return ExportFileUsingCallbacks(document, { prologue = function() writer('%% This document automatically generated by '.. 'WordGrinder '..VERSION..'.\n') writer('\\documentclass{article}\n') writer('\\usepackage{xunicode, setspace, xltxtra}\n') writer('\\sloppy\n') writer('\\onehalfspacing\n') writer('\\begin{document}\n') writer('\\title{', untex(Document.name), '}\n') writer('\\author{(no author)}\n') writer('\\maketitle\n') end, rawtext = function(s) writer(s) end, text = function(s) writer(untex(s)) end, notext = function(s) writer('\\paragraph{}') end, italic_on = function() writer('\\textit{') end, italic_off = function() writer('}') end, bold_on = function() writer('\\textbf{') end, bold_off = function() writer('}') end, underline_on = function() writer('\\underline{') end, underline_off = function() writer('}') end, list_start = function() writer('\\begin{itemize}\n') end, list_end = function() writer('\\end{itemize}\n') end, paragraph_start = function(style) writer(style_tab[style][1] or "") end, paragraph_end = function(style) writer(style_tab[style][2] or "") writer('\n') end, epilogue = function() writer('\\end{document}\n') end }) end function Cmd.ExportLatexFile(filename) return ExportFileWithUI(filename, "Export LaTeX File", ".tex", callback) end wordgrinder-0.5.1.orig/src/lua/export/opendocument.lua0000644000000000000000000002550612243442530017773 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local table_concat = table.concat local writezip = wg.writezip ----------------------------------------------------------------------------- -- The exporter itself. local function unhtml(s) s = s:gsub("&", "&") s = s:gsub("<", "<") s = s:gsub(">", ">") s = s:gsub("%s", "") return s end local style_tab = { ["H1"] = {false, '', ''}, ["H2"] = {false, '', ''}, ["H3"] = {false, '', ''}, ["H4"] = {false, '', ''}, ["P"] = {false, '', ''}, ["L"] = {false, '', ''}, ["LB"] = {false, '', ''}, ["Q"] = {false, '', ''}, ["V"] = {false, '', ''}, ["RAW"] = {false, '', ''}, ["PRE"] = {false, '', ''} } local function callback(writer, document) local settings = DocumentSet.addons.htmlexport local currentpara = nil function changepara(newpara) local currentstyle = style_tab[currentpara] local newstyle = style_tab[newpara] if (newpara ~= currentpara) or not newpara or not currentstyle[1] or not newstyle[1] then if currentstyle then writer(currentstyle[3]) end writer("\n") if newstyle then writer(newstyle[2]) end currentpara = newpara else writer("\n") end end return ExportFileUsingCallbacks(document, { prologue = function() writer( [[ ]] ) end, epilogue = function() changepara(nil) writer('\n') end, rawtext = function(s) writer(s) end, text = function(s) writer(unhtml(s)) end, notext = function(s) end, italic_on = function() writer('') end, italic_off = function() writer("") end, bold_on = function() writer('') end, bold_off = function() writer("") end, underline_on = function() writer('') end, underline_off = function() writer("") end, list_start = function() end, list_end = function() end, paragraph_start = function(style) changepara(style) end, paragraph_end = function(style) end, }) end local function export_odt_with_ui(filename, title, extension) if not filename then filename = Document.name if filename then if not filename:find("%..-$") then filename = filename .. extension else filename = filename:gsub("%..-$", extension) end else filename = "(unnamed)" end filename = FileBrowser(title, "Export as:", true, filename) if not filename then return false end if filename:find("/[^.]*$") then filename = filename .. extension end end ImmediateMessage("Exporting...") local content = {} local writer = function(s) content[#content+1] = s end callback(writer, Document) content = table_concat(content) local xml = { ["mimetype"] = "application/vnd.oasis.opendocument.text", ["META-INF/manifest.xml"] = [[ ]], ["styles.xml"] = [[ ]], ["settings.xml"] = [[ ]], ["meta.xml"] = [[ ]], ["content.xml"] = content } if not writezip(filename, xml) then ModalMessage(nil, "Unable to open the output file "..e..".") QueueRedraw() return false end QueueRedraw() return true end function Cmd.ExportODTFile(filename) return export_odt_with_ui(filename, "Export ODT File", ".odt") end wordgrinder-0.5.1.orig/src/lua/export/text.lua0000644000000000000000000000166212243251035016252 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local function callback(writer, document) return ExportFileUsingCallbacks(document, { prologue = function() end, rawtext = function(s) writer(s) end, text = function(s) writer(s) end, notext = function(s) end, italic_on = function() end, italic_off = function() end, underline_on = function() end, underline_off = function() end, bold_on = function() end, bold_off = function() end, list_start = function() end, list_end = function() end, paragraph_start = function(style) end, paragraph_end = function(style) writer('\n') end, epilogue = function() end }) end function Cmd.ExportTextFile(filename) return ExportFileWithUI(filename, "Export Text File", ".txt", callback) end wordgrinder-0.5.1.orig/src/lua/export/troff.lua0000644000000000000000000000634212250440472016411 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local NextCharInWord = wg.nextcharinword local ReadU8 = wg.readu8 local string_len = string.len local string_char = string.char local string_format = string.format local string_gsub = string.gsub local style_tab = { ["H1"] = '.NH 1', ["H2"] = '.NH 2', ["H3"] = '.NH 3', ["H4"] = '.NH 4', ["P"] = '.LP', ["L"] = '.IP', ["LB"] = '.IP \\[bu]', ["Q"] = '.IP', ["V"] = '.IP', ["RAW"] = '', ["PRE"] = '.LD 1', } local function callback(writer, document) local currentstyle = nil local ul = false local it = false local bf = false local embedded = false local linestart = true local function newline() if not linestart then writer("\n") end linestart = true end local function emit_text(s) if linestart then s = string_gsub(s, "^([.'])", '\\%1') s = string_gsub(s, '^%s+', '') linestart = false end if embedded then s = string_gsub(s, '"', '\\"') end s = string_gsub(s, '\\', '\\\\') local o = 1 local n = string_len(s) while (o <= n) do local c = ReadU8(s, o) o = NextCharInWord(s, o) if (c < 127) then writer(string_char(c)) else writer(string_format('\\[u%04X]', c)) end end end local function changestate(newit, newul, newbf) if newul and not ul then newline() writer('.UL "') embedded = true linestart = false end if newit and not newbf then writer('\\fI') linestart = false elseif newit and newbf then writer('\\f(BI') linestart = false elseif not newit and newbf then writer('\\fB') linestart = false elseif not newit and not newbf and (it or bf) then writer('\\fR') linestart = false end if not newul and ul then writer('"\n') embedded = false linestart = true end it = newit ul = newul bf = newbf end return ExportFileUsingCallbacks(document, { prologue = function() writer('.\\" This document automatically generated by '.. 'WordGrinder '..VERSION..'.\n') writer('.\\" Use the .ms macro package!\n') writer('.TL\n') emit_text(Document.name) writer('\n') linestart = true end, text = emit_text, rawtext = function(s) writer(s) end, notext = function(s) end, italic_on = function() changestate(true, ul, bf) end, italic_off = function() changestate(false, ul, bf) end, underline_on = function() changestate(it, true, bf) end, underline_off = function() changestate(it, false, bf) end, bold_on = function() changestate(it, ul, true) end, bold_off = function() changestate(it, ul, false) end, list_start = function() end, list_end = function() end, paragraph_start = function(style) if (currentstyle ~= "PRE") or (style ~= "PRE") then if (currentstyle == "PRE") then writer(".DE\n") end writer(style_tab[style] or ".LP") writer('\n') end linestart = true currentstyle = style end, paragraph_end = function(style) newline() end, epilogue = function() end }) end function Cmd.ExportTroffFile(filename) return ExportFileWithUI(filename, "Export Troff File", ".tr", callback) end wordgrinder-0.5.1.orig/src/lua/export.lua0000644000000000000000000000672712243442530015277 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local BOLD = wg.BOLD local ParseWord = wg.parseword local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local string_lower = string.lower local time = wg.time -- Renders the document by calling the appropriate functions on the cb -- table. function ExportFileUsingCallbacks(document, cb) cb.prologue() local listmode = false local rawmode = false local italic, underline, bold local olditalic, oldunderline, oldbold local firstword local wordbreak local emptyword local wordwriter = function (style, text) italic = bit(style, ITALIC) underline = bit(style, UNDERLINE) bold = bit(style, BOLD) local writer if rawmode then writer = cb.rawtext else writer = cb.text end if not italic and olditalic then cb.italic_off() end if not underline and oldunderline then cb.underline_off() end if not bold and oldbold then cb.bold_off() end if wordbreak then writer(' ') wordbreak = false end if bold and not oldbold then cb.bold_on() end if underline and not oldunderline then cb.underline_on() end if italic and not olditalic then cb.italic_on() end writer(text) emptyword = false olditalic = italic oldunderline = underline oldbold = bold end for _, paragraph in ipairs(Document) do local style = paragraph.style local name = style.name if (name == "L") or (name == "LB") then if not listmode then cb.list_start() listmode = true end elseif listmode then cb.list_end() listmode = false end rawmode = (name == "RAW") cb.paragraph_start(name) if (#paragraph == 1) and (#paragraph[1].text == 0) then cb.notext() else firstword = true wordbreak = false olditalic = false oldunderline = false oldbold = false for wn, word in ipairs(paragraph) do if firstword then firstword = false else wordbreak = true end emptyword = true italic = false underline = false bold = false ParseWord(word.text, 0, wordwriter) -- FIXME if emptyword then wordwriter(0, "") end end if italic then cb.italic_off() end if underline then cb.underline_off() end if bold then cb.bold_off() end end cb.paragraph_end(name) end if listmode then cb.list_end() end cb.epilogue() end -- Prompts the user to export a document, and then calls -- exportcb(writer, document) to actually do the work. function ExportFileWithUI(filename, title, extension, callback) if not filename then filename = Document.name if filename then if not filename:find("%..-$") then filename = filename .. extension else filename = filename:gsub("%..-$", extension) end else filename = "(unnamed)" end filename = FileBrowser(title, "Export as:", true, filename) if not filename then return false end if filename:find("/[^.]*$") then filename = filename .. extension end end ImmediateMessage("Exporting...") local fp, e = io.open(filename, "w") if not fp then ModalMessage(nil, "Unable to open the output file "..e..".") QueueRedraw() return false end local fpw = fp.write local writer = function(...) fpw(fp, ...) end callback(writer, Document) fp:close() QueueRedraw() return true end wordgrinder-0.5.1.orig/src/lua/fileio.lua0000644000000000000000000002524412246725752015235 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ParseWord = wg.parseword local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local time = wg.time local compress = wg.compress local decompress = wg.decompress local writeu8 = wg.writeu8 local readu8 = wg.readu8 local MAGIC = "WordGrinder dumpfile v1: this is not a text file!" local ZMAGIC = "WordGrinder dumpfile v2: this is not a text file!" local STOP = 0 local TABLE = 1 local BOOLEANTRUE = 2 local BOOLEANFALSE = 3 local STRING = 4 local NUMBER = 5 local CACHE = 6 local NEGNUMBER = 7 local BRIEFWORD = 8 local DOCUMENTSETCLASS = 100 local DOCUMENTCLASS = 101 local PARAGRAPHCLASS = 102 local WORDCLASS = 103 local MENUCLASS = 104 local function writetostream(object, writes, writei) local type_lookup = { [DocumentSetClass] = DOCUMENTSETCLASS, [DocumentClass] = DOCUMENTCLASS, [ParagraphClass] = PARAGRAPHCLASS, [WordClass] = WORDCLASS, [MenuClass] = MENUCLASS, } local cache = {} local cacheid = 1 local function save(t) if cache[t] then writei(CACHE) writei(cache[t]) return end cache[t] = cacheid cacheid = cacheid + 1 if (type(t) == "table") then local m = getmetatable(t) if m then m = type_lookup[m.__index] else m = TABLE end if (m == WORDCLASS) then writei(BRIEFWORD) save(t.text) else writei(m) writei(#t) for _, i in ipairs(t) do save(i) end for k, v in pairs(t) do if (type(k) ~= "number") then save(k) save(v) end end writei(STOP) end elseif (type(t) == "boolean") then if t then writei(BOOLEANTRUE) else writei(BOOLEANFALSE) end elseif (type(t) == "string") then writei(STRING) writei(#t) writes(t) elseif (type(t) == "number") then if (t >= 0) then writei(NUMBER) writei(t) else writei(NEGNUMBER) writei(-t) end else error("unsupported type "..type(t)) end end save(object) return true end function SaveDocumentSetRaw(filename) local fp, e = io.open(filename, "wb") if not fp then return false, e end DocumentSet:purge() local fpw = fp.write local ss = {} local writes = function(s) if (type(s) == "number") then s = writeu8(s) end ss[#ss+1] = s end local writei = function(s) s = writeu8(s) ss[#ss+1] = s end fp:write(ZMAGIC, "\n") local r = writetostream(DocumentSet, writes, writei) local s = compress(table.concat(ss)) fp:write(s) fp:close() return r end function Cmd.SaveCurrentDocumentAs(filename) if not filename then filename = FileBrowser("Save Document Set", "Save as:", true) if not filename then return false end if filename:find("/[^.]*$") then filename = filename .. ".wg" end end DocumentSet.name = filename ImmediateMessage("Saving...") DocumentSet:clean() local r, e = SaveDocumentSetRaw(DocumentSet.name) if not r then ModalMessage("Save failed", "The document could not be saved: "..e) else NonmodalMessage("Save succeeded.") end return r end function Cmd.SaveCurrentDocument() local name = DocumentSet.name if not name then name = FileBrowser("Save Document Set", "Save as:", true) if not name then return false end if name:find("/[^.]*$") then name = name .. ".wg" end DocumentSet.name = name end return Cmd.SaveCurrentDocumentAs(name) end local function loadfromstream(fp) local cache = {} local load local function populate_table(t) local n = tonumber(fp:read("*l")) for i = 1, n do t[i] = load() end while true do local k = load() if not k then break end t[k] = load() end return t end local load_cb = { ["DS"] = function() local t = {} setmetatable(t, {__index = DocumentSetClass}) cache[#cache + 1] = t return populate_table(t) end, ["D"] = function() local t = {} setmetatable(t, {__index = DocumentClass}) cache[#cache + 1] = t return populate_table(t) end, ["P"] = function() local t = {} setmetatable(t, {__index = ParagraphClass}) cache[#cache + 1] = t return populate_table(t) end, ["W"] = function() local t = {} setmetatable(t, {__index = WordClass}) cache[#cache + 1] = t return populate_table(t) end, ["M"] = function() local t = {} setmetatable(t, {__index = MenuClass}) cache[#cache + 1] = t return populate_table(t) end, ["T"] = function() local t = {} cache[#cache + 1] = t return populate_table(t) end, ["S"] = function() local s = fp:read("*l") cache[#cache + 1] = s return s end, ["N"] = function() local n = tonumber(fp:read("*l")) cache[#cache + 1] = n return n end, ["B"] = function() local s = fp:read("*l") s = (s == "T") cache[#cache + 1] = s return s end, ["."] = function() return nil end } load = function() local s = fp:read("*l") if not s then error("unexpected EOF when reading file") end local n = tonumber(s) if n then return cache[n] end local f = load_cb[s] if not f then error("can't load type "..s) end return f() end return load() end local function loadfromstreamz(fp) local cache = {} local load local data = decompress(fp:read("*a")) local offset = 1 local function populate_table(t) local n n, offset = readu8(data, offset) for i = 1, n do t[i] = load() end while true do local k = load() if not k then break end t[k] = load() end return t end local load_cb = { [CACHE] = function() local n n, offset = readu8(data, offset) return cache[n] end, [DOCUMENTSETCLASS] = function() local t = {} setmetatable(t, {__index = DocumentSetClass}) cache[#cache + 1] = t return populate_table(t) end, [DOCUMENTCLASS] = function() local t = {} setmetatable(t, {__index = DocumentClass}) cache[#cache + 1] = t return populate_table(t) end, [PARAGRAPHCLASS] = function() local t = {} setmetatable(t, {__index = ParagraphClass}) cache[#cache + 1] = t return populate_table(t) end, [WORDCLASS] = function() local t = {} setmetatable(t, {__index = WordClass}) cache[#cache + 1] = t return populate_table(t) end, [BRIEFWORD] = function() local t = {} setmetatable(t, {__index = WordClass}) cache[#cache + 1] = t t.text = load() return t end, [MENUCLASS] = function() local t = {} setmetatable(t, {__index = MenuClass}) cache[#cache + 1] = t return populate_table(t) end, [TABLE] = function() local t = {} cache[#cache + 1] = t return populate_table(t) end, [STRING] = function() local n n, offset = readu8(data, offset) local s = data:sub(offset, offset+n-1) offset = offset + n cache[#cache + 1] = s return s end, [NUMBER] = function() local n n, offset = readu8(data, offset) cache[#cache + 1] = n return n end, [NEGNUMBER] = function() local n n, offset = readu8(data, offset) n = -n cache[#cache + 1] = n return n end, [BOOLEANTRUE] = function() cache[#cache + 1] = true return true end, [BOOLEANFALSE] = function() cache[#cache + 1] = false return false end, [STOP] = function() return nil end } load = function() local n n, offset = readu8(data, offset) local f = load_cb[n] if not f then error("can't load type "..n.." at offset "..offset) end return f() end return load() end local function loaddocument(filename) local fp, e = io.open(filename, "rb") if not fp then return nil, ("'"..filename.."' could not be opened: "..e) end local loader = nil local magic = fp:read("*l") if (magic == MAGIC) then loader = loadfromstream elseif (magic == ZMAGIC) then loader = loadfromstreamz else fp:close() return nil, ("'"..filename.."' is not a valid WordGrinder file.") end local d, e = loader(fp) fp:close() if not d then return nil, e end -- This should not be necessary, but we do it anyway just to be sure. d:purge() -- Even if the changed flag was set in the document on disk, remove it. d:clean() d.name = filename return d end function Cmd.LoadDocumentSet(filename) if not ConfirmDocumentErasure() then return false end if not filename then filename = FileBrowser("Load Document Set", "Load file:", false) if not filename then return false end end ImmediateMessage("Loading...") local d, e = loaddocument(filename) if not d then if not e then e = "The load failed, probably because the file could not be opened." end ModalMessage(nil, e) QueueRedraw() return false end DocumentSet = d Document = d.current ResizeScreen() FireEvent(Event.DocumentLoaded) RebuildParagraphStylesMenu(DocumentSet.styles) RebuildDocumentsMenu(DocumentSet.documents) QueueRedraw() return true end ----------------------------------------------------------------------------- -- Cause the document to get upgraded, if necessary. do local function cb(event, token) local fileformat = DocumentSet.fileformat or 1 if (fileformat ~= FILEFORMAT) then ModalMessage("Upgrading document", "You are trying to open a file belonging to an earlier ".. "version of WordGrinder. That's not a problem, but if you ".. "save the file again it may not work on the old version. ".. "Also, all keybindings defined in this file will get reset ".. "to their default values.") ImmediateMessage("Upgrading...") FireEvent(Event.DocumentUpgrade, fileformat, FILEFORMAT) DocumentSet.fileformat = FILEFORMAT DocumentSet.menu = CreateMenu() DocumentSet:touch() end -- This happens here because it must happen before the result of the -- DocumentLoaded handlers fire, but after the DocumentUpgrade -- handlers. It's not really a very elegant place for it. FireEvent(Event.RegisterAddons) end AddEventListener(Event.DocumentLoaded, cb) end ----------------------------------------------------------------------------- -- Upgrade the document, if necessary. do local function cb(event, token, oldversion, newversion) -- Upgrade version 1 to 2. if (oldversion < 2) then -- Update wordcount. for _, document in ipairs(DocumentSet) do local wc = 0 for _, p in ipairs(document) do wc = wc + #p end document.wordcount = wc end -- Status bar defaults to on. DocumentSet.statusbar = true end -- Upgrade version 2 to 3. if (oldversion < 3) then -- Idle time defaults to 3. DocumentSet.idletime = 3 -- Add the addons setting table. DocumentSet.addons = {} end end AddEventListener(Event.DocumentUpgrade, cb) end wordgrinder-0.5.1.orig/src/lua/forms.lua0000644000000000000000000003044212243254677015110 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local Write = wg.write local GotoXY = wg.gotoxy local GetStringWidth = wg.getstringwidth local GetBoundedString = wg.getboundedstring local GetBytesOfCharacter = wg.getbytesofcharacter local SetNormal = wg.setnormal local SetBold = wg.setbold local SetBright = wg.setbright local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local string_rep = string.rep Form = {} -- Atoms. Form.Left = {} Form.Right = {} Form.Centre = {} Form.Center = Form.Centre Form.Large = {} local function min(a, b) if (a < b) then return a else return b end end local function max(a, b) if (a > b) then return a else return b end end local function makewidgetclass(class) return function(table) setmetatable(table, {__index = class}) table.class = class return table end end Form.Divider = makewidgetclass { draw = function(self) Write(self.realx1, self.realy1, string_rep("─", self.realwidth)) end } Form.WrappedLabel = makewidgetclass { draw = function(self) local words = ParseStringIntoWords(self.value) local paragraph = CreateParagraph(nil, words) local lines = paragraph:wrap(self.realwidth) for i = 1, #lines do paragraph:renderLine(lines[i], self.realx1, self.realy1+i-1) end end, calculate_height = function(self) local words = ParseStringIntoWords(self.value) local paragraph = CreateParagraph(nil, words) local lines = paragraph:wrap(self.realwidth) return #lines end, } Form.Label = makewidgetclass { align = Form.Centre, draw = function(self) local xo if (self.align == Form.Centre) then xo = int((self.realwidth - GetStringWidth(self.value)) / 2) elseif (self.align == Form.Left) then xo = 0 elseif (self.align == Form.Right) then xo = self.realwidth - GetStringWidth(self.value) end Write(self.realx1, self.realy1, string_rep(" ", self.realwidth)) Write(self.realx1 + xo, self.realy1, self.value) end } local checkbox_toggle = function(self, key) self.value = not self.value self:draw() end Form.Checkbox = makewidgetclass { value = false, label = "Checkbox", focusable = true, draw = function(self) local s if self.value then s = "> YES" else s = "> NO " end Write(self.realx1, self.realy1, GetBoundedString(self.label, self.realwidth - 2)) SetBright() Write(self.realx2, self.realy1, s) SetNormal() if self.focus then GotoXY(self.realx2, self.realy1) end end, [" "] = checkbox_toggle } Form.TextField = makewidgetclass { focusable = true, init = function(self) self.cursor = self.cursor or (self.value:len() + 1) self.offset = self.offset or 1 end, draw = function(self) SetBright() Write(self.realx1, self.realy1 + 1, string_rep("▔", self.realwidth)) Write(self.realx1, self.realy1, string_rep(" ", self.realwidth)) SetNormal() -- If the cursor is to the left of the visible area, adjust. if (self.cursor < self.offset) then self.offset = self.cursor end -- If the cursor is to the right of the visible area, adjust. (This is -- very crude, but I'm not sure there's a more elegant way of doing -- it.) while true do local xo = GetStringWidth(self.value:sub(self.offset, self.cursor)) if (xo <= self.realwidth) then break end local b = GetBytesOfCharacter(self.value:byte(self.offset)) self.offset = self.offset + b end -- Draw the visible bit of the string. local s = GetBoundedString(self.value:sub(self.offset), self.realwidth) SetBright() Write(self.realx1, self.realy1, s) SetNormal() if self.focus then GotoXY(self.realx1 + GetStringWidth(s:sub(1, self.cursor-self.offset)), self.realy1) end end, ["KEY_LEFT"] = function(self, key) if (self.cursor > 1) then while true do self.cursor = self.cursor - 1 if (GetBytesOfCharacter(self.value:byte(self.cursor)) ~= 0) then break end end self:draw() end return "nop" end, ["KEY_RIGHT"] = function(self, key) if (self.cursor <= self.value:len()) then self.cursor = self.cursor + GetBytesOfCharacter(self.value:byte(self.cursor)) self:draw() end return "nop" end, ["KEY_HOME"] = function(self, key) self.cursor = 1 self:draw() return "nop" end, ["KEY_END"] = function(self, key) self.cursor = self.value:len() + 1 self:draw() return "nop" end, ["KEY_BACKSPACE"] = function(self, key) if (self.cursor > 1) then local w while true do self.cursor = self.cursor - 1 w = GetBytesOfCharacter(self.value:byte(self.cursor)) if (w ~= 0) then break end end self.value = self.value:sub(1, self.cursor - 1) .. self.value:sub(self.cursor + w) self:draw() end return "nop" end, ["KEY_DELETE"] = function(self, key) local v = self.value:byte(self.cursor) if v then local w = GetBytesOfCharacter(self.value:byte(self.cursor)) self.value = self.value:sub(1, self.cursor - 1) .. self.value:sub(self.cursor + w) self:draw() end return "nop" end, ["KEY_^U"] = function(self, key) self.cursor = 1 self.offset = 1 self.value = "" self:draw() return "nop" end, key = function(self, key) if not key:match("^KEY_") then self.value = self.value:sub(1, self.cursor-1) .. key .. self.value:sub(self.cursor) self.cursor = self.cursor + GetBytesOfCharacter(key:byte(1)) self:draw() return "nop" end end, } Form.Browser = makewidgetclass { focusable = true, init = function(self) self.cursor = self.cursor or 1 self.offset = self.offset or 0 end, _adjustOffset = function(self) local h = self.realheight if (self.offset == 0) then self.offset = self.cursor - int(h/2) end self.offset = min(self.offset, self.cursor) self.offset = max(self.offset, self.cursor - (h-2)) self.offset = min(self.offset, #self.data - (h-2)) self.offset = max(self.offset, 1) end, changed = function(self) return "nop" end, draw = function(self) local x = self.realx1 local y = self.realy1 local w = self.realwidth local h = self.realheight -- Draw the box. do local border = string_rep("─", w - 2) SetBright() Write(x, y, "┌") Write(x+1, y, border) Write(x+w-1, y, "┐") for i = 1, h-1 do Write(x, y+i, "│") Write(x+w-1, y+i, "│") end Write(x, y+h, "└") Write(x+1, y+h, border) Write(x+w-1, y+h, "┘") SetNormal() end self:_adjustOffset() -- Draw the data. local space = string_rep(" ", w - 2) for i = 0, h-2 do local index = self.offset + i local item = self.data[index] if not item then break end if (index == self.cursor) then SetReverse() else SetNormal() end Write(x+1, y+1+i, space) local s = GetBoundedString(item.label, w-4) Write(x+2, y+1+i, s) if (#self.data > (h-2)) then SetNormal() SetBright() s = "│" local yf = (i+1) * #self.data / (h-1) if (yf >= self.offset) and (yf <= (self.offset + h-2)) then s = "║" end Write(x+w-1, y+1+i, s) end SetNormal() end SetNormal() end, ["KEY_UP"] = function(self, key) if (self.cursor > 1) then self.cursor = self.cursor - 1 self:draw() return self:changed() end return "nop" end, ["KEY_DOWN"] = function(self, key) if (self.cursor < #self.data) then self.cursor = self.cursor + 1 self:draw() return self:changed() end return "nop" end, ["KEY_PGUP"] = function(self, key) local oldcursor = self.cursor self.cursor = oldcursor - int(self.realheight/2) if (self.cursor < 1) then self.cursor = 1 end if (self.cursor ~= oldcursor) then self:draw() return self:changed() end return "nop" end, ["KEY_PGDN"] = function(self, key) local oldcursor = self.cursor self.cursor = oldcursor + int(self.realheight/2) if (self.cursor > #self.data) then self.cursor = #self.data end if (self.cursor ~= oldcursor) then self:draw() return self:changed() end return "nop" end, } local standard_actions = { ["KEY_UP"] = function(dialogue, key) if dialogue.focus then local f = dialogue.focus - 1 while (f ~= dialogue.focus) do if (f == 0) then f = #dialogue end local widget = dialogue[f] if widget.focusable then dialogue.focus = f return "redraw" end f = f - 1 end end return "nop" end, ["KEY_DOWN"] = function(dialogue, key) if dialogue.focus then local f = dialogue.focus + 1 while (f ~= dialogue.focus) do if (f > #dialogue) then f = 1 end local widget = dialogue[f] if widget.focusable then dialogue.focus = f return "redraw" end f = f + 1 end end return "nop" end } local function resolvesize(size, bound) if (size < 0) then return size + bound else return size end end local function findaction(table, object, key) local action = table[key] if action and (type(action) == "function") then action = action(object, key) end if not action and table.key then action = table.key(object, key) end return action end function Form.Run(dialogue, redraw, helptext) -- Ensure the screen is properly sized. ResizeScreen() -- Find a widget to give the focus to. if not dialogue.focus then for i, widget in ipairs(dialogue) do if widget.focusable then dialogue.focus = i break end end end -- Initialise any widgets that need it. for _, widget in ipairs(dialogue) do if widget.init then widget:init() end end -- Redraw the backdrop. if redraw then redraw() end -- Size the dialogue. if (dialogue.width == Form.Large) then dialogue.realwidth = int(ScreenWidth * 6/7) else dialogue.realwidth = dialogue.width end if (dialogue.height == Form.Large) then dialogue.realheight = int(ScreenHeight * 5/6) else dialogue.realheight = dialogue.height end -- Is this a stretchy dialogue? if dialogue.stretchy then -- Automatically scale the height depending on a 'stretchy' widget. for _, widget in ipairs(dialogue) do if (widget.y1 > 0) and (widget.y2 < 0) then widget.realx1 = resolvesize(widget.x1, dialogue.realwidth) widget.realx2 = resolvesize(widget.x2, dialogue.realwidth) widget.realwidth = widget.realx2 - widget.realx1 local h = 1 if widget.calculate_height then h = widget:calculate_height() end dialogue.realheight = dialogue.height + h break end end end -- Place the dialogue. dialogue.realx = int(ScreenWidth/2 - dialogue.realwidth/2) dialogue.realy = int(ScreenHeight/2 - dialogue.realheight/2) -- Place all widgets in the dialogue. for _, widget in ipairs(dialogue) do widget.realx1 = resolvesize(widget.x1, dialogue.realwidth) + dialogue.realx widget.realy1 = resolvesize(widget.y1, dialogue.realheight) + dialogue.realy widget.realx2 = resolvesize(widget.x2, dialogue.realwidth) + dialogue.realx widget.realy2 = resolvesize(widget.y2, dialogue.realheight) + dialogue.realy widget.realwidth = widget.realx2 - widget.realx1 widget.realheight = widget.realy2 - widget.realy1 end -- Draw the dialogue itself. do local sizeadjust = 0 if helptext then sizeadjust = 1 end DrawTitledBox(dialogue.realx - 1, dialogue.realy - 1, dialogue.realwidth, dialogue.realheight + sizeadjust, dialogue.title) if helptext then CentreInField(dialogue.realx, dialogue.realy + dialogue.realheight, dialogue.realwidth, "<"..helptext..">") end end -- Draw the widgets. GotoXY(ScreenWidth-1, ScreenHeight-1) for i, widget in ipairs(dialogue) do widget.focus = (i == dialogue.focus) widget:draw() end -- Process keys. while true do local key = wg.getchar() if (key == "KEY_RESIZE") then ResizeScreen() return Form.Run(dialogue, redraw, helptext) end local action = nil if dialogue.focus then local w = dialogue[dialogue.focus] action = findaction(w, w, key) end if not action then action = findaction(dialogue, dialogue, key) or findaction(standard_actions, dialogue, key) end if (action == "redraw") then return Form.Run(dialogue, redraw, helptext) elseif (action == "cancel") then return false elseif (action == "confirm") then return true end end end -- Test code function Form.Test() FileBrowser("Title", "Load file:", false) end wordgrinder-0.5.1.orig/src/lua/html.lua0000644000000000000000000001366311523520773014725 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local WriteU8 = wg.writeu8 local string_find = string.find HTMLEntities = { ["&"] = "&", [">"] = ">", ["<"] = "<", ["""] = '"', ["´"] = "´", ["¸"] = "¸", ["ˆ"] = "ˆ", ["¯"] = "¯", ["·"] = "·", ["˜"] = "˜", ["¨"] = "¨", ["Á"] = "Á", ["á"] = "á", ["Â"] = "Â", ["â"] = "â", ["Æ"] = "Æ", ["æ"] = "æ", ["À"] = "À", ["à"] = "à", ["Å"] = "Å", ["å"] = "å", ["Ã"] = "Ã", ["ã"] = "ã", ["Ä"] = "Ä", ["ä"] = "ä", ["Ç"] = "Ç", ["ç"] = "ç", ["É"] = "É", ["é"] = "é", ["Ê"] = "Ê", ["ê"] = "ê", ["È"] = "È", ["è"] = "è", ["Ð"] = "Ð", ["ð"] = "ð", ["Ë"] = "Ë", ["ë"] = "ë", ["Í"] = "Í", ["í"] = "í", ["Î"] = "Î", ["î"] = "î", ["Ì"] = "Ì", ["ì"] = "ì", ["Ï"] = "Ï", ["ï"] = "ï", ["Ñ"] = "Ñ", ["ñ"] = "ñ", ["Ó"] = "Ó", ["ó"] = "ó", ["Ô"] = "Ô", ["ô"] = "ô", ["Œ"] = "Œ", ["œ"] = "œ", ["Ò"] = "Ò", ["ò"] = "ò", ["Ø"] = "Ø", ["ø"] = "ø", ["Õ"] = "Õ", ["õ"] = "õ", ["Ö"] = "Ö", ["ö"] = "ö", ["Š"] = "Š", ["š"] = "š", ["ß"] = "ß", ["Þ"] = "Þ", ["þ"] = "þ", ["Ú"] = "Ú", ["ú"] = "ú", ["Û"] = "Û", ["û"] = "û", ["Ù"] = "Ù", ["ù"] = "ù", ["Ü"] = "Ü", ["ü"] = "ü", ["Ý"] = "Ý", ["ý"] = "ý", ["ÿ"] = "ÿ", ["Ÿ"] = "Ÿ", ["¢"] = "¢", ["¤"] = "¤", ["€"] = "€", ["£"] = "£", ["¥"] = "¥", ["¦"] = "¦", ["•"] = "•", ["©"] = "©", ["†"] = "†", ["‡"] = "‡", ["⁄"] = "⁄", ["…"] = "…", ["¡"] = "¡", ["ℑ"] = "ℑ", ["¿"] = "¿", ["‎"] = "", ["—"] = "—", ["–"] = "–", ["¬"] = "¬", ["‾"] = "‾", ["ª"] = "ª", ["º"] = "º", ["¶"] = "¶", ["‰"] = "‰", ["′"] = "′", ["″"] = "″", ["ℜ"] = "ℜ", ["®"] = "®", ["‏"] = "", ["§"] = "§", ["­"] = "­", ["¹"] = "¹", ["™"] = "™", ["℘"] = "℘", ["„"] = "„", ["«"] = "«", ["“"] = "“", ["‹"] = "‹", ["‘"] = "‘", ["»"] = "»", ["”"] = "”", ["›"] = "›", ["’"] = "’", ["‚"] = "‚", -- Some of these space constants are magic. Edit with care. [" "] = " ", [" "] = " ", [" "] = " ", [" "] = " ", ["‍"] = "‍", ["‌"] = "‌", ["°"] = "°", ["÷"] = "÷", ["½"] = "½", ["¼"] = "¼", ["¾"] = "¾", ["≥"] = "≥", ["≤"] = "≤", ["−"] = "−", ["²"] = "²", ["³"] = "³", ["×"] = "×", ["ℵ"] = "ℵ", ["∧"] = "∧", ["∠"] = "∠", ["≈"] = "≈", ["∩"] = "∩", ["≅"] = "≅", ["∪"] = "∪", ["∅"] = "∅", ["≡"] = "≡", ["∃"] = "∃", ["ƒ"] = "ƒ", ["∀"] = "∀", ["∞"] = "∞", ["∫"] = "∫", ["∈"] = "∈", ["⟨"] = "〈", ["⌈"] = "⌈", ["⌊"] = "⌊", ["∗"] = "∗", ["µ"] = "µ", ["∇"] = "∇", ["≠"] = "≠", ["∋"] = "∋", ["∉"] = "∉", ["⊄"] = "⊄", ["⊕"] = "⊕", ["∨"] = "∨", ["⊗"] = "⊗", ["∂"] = "∂", ["⊥"] = "⊥", ["±"] = "±", ["∏"] = "∏", ["∝"] = "∝", ["√"] = "√", ["⟩"] = "〉", ["⌉"] = "⌉", ["⌋"] = "⌋", ["⋅"] = "⋅", ["∼"] = "∼", ["⊂"] = "⊂", ["⊆"] = "⊆", ["∑"] = "∑", ["⊃"] = "⊃", ["⊇"] = "⊇", ["∴"] = "∴", ["Α"] = "Α", ["α"] = "α", ["Β"] = "Β", ["β"] = "β", ["Χ"] = "Χ", ["χ"] = "χ", ["Δ"] = "Δ", ["δ"] = "δ", ["Ε"] = "Ε", ["ε"] = "ε", ["Η"] = "Η", ["η"] = "η", ["Γ"] = "Γ", ["γ"] = "γ", ["Ι"] = "Ι", ["ι"] = "ι", ["Κ"] = "Κ", ["κ"] = "κ", ["Λ"] = "Λ", ["λ"] = "λ", ["Μ"] = "Μ", ["μ"] = "μ", ["Ν"] = "Ν", ["ν"] = "ν", ["Ω"] = "Ω", ["ω"] = "ω", ["Ο"] = "Ο", ["ο"] = "ο", ["Φ"] = "Φ", ["φ"] = "φ", ["Π"] = "Π", ["π"] = "π", ["ϖ"] = "ϖ", ["Ψ"] = "Ψ", ["ψ"] = "ψ", ["Ρ"] = "Ρ", ["ρ"] = "ρ", ["Σ"] = "Σ", ["σ"] = "σ", ["ς"] = "ς", ["Τ"] = "Τ", ["τ"] = "τ", ["Θ"] = "Θ", ["θ"] = "θ", ["ϑ"] = "ϑ", ["ϒ"] = "ϒ", ["Υ"] = "Υ", ["υ"] = "υ", ["Ξ"] = "Ξ", ["ξ"] = "ξ", ["Ζ"] = "Ζ", ["ζ"] = "ζ", ["↵"] = "↵", ["↓"] = "↓", ["⇓"] = "⇓", ["↔"] = "↔", ["⇔"] = "⇔", ["←"] = "←", ["⇐"] = "⇐", ["→"] = "→", ["⇒"] = "⇒", ["↑"] = "↑", ["⇑"] = "⇑", ["♣"] = "♣", ["♦"] = "♦", ["♥"] = "♥", ["♠"] = "♠", ["◊"] = "◊" } function DecodeHTMLEntity(s) local e = HTMLEntities[s] if e then return e end local _, _, e = string_find(s, "^&#(%w*);") if not e then return nil end e = tonumber("0"..e) if not e then return nil end return WriteU8(e) end wordgrinder-0.5.1.orig/src/lua/import/0000755000000000000000000000000012251160512014545 5ustar wordgrinder-0.5.1.orig/src/lua/import/html.lua0000644000000000000000000000716712243251035016231 0ustar -- © 2008-2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local BOLD = wg.BOLD local ParseWord = wg.parseword local WriteU8 = wg.writeu8 local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local string_char = string.char local string_find = string.find local string_sub = string.sub local table_concat = table.concat ----------------------------------------------------------------------------- -- The importer itself. local function loadhtmlfile(fp) local data = fp:read("*a") -- Collapse whitespace; this makes things far easier to parse. data = data:gsub("[\t\f]", " ") data = data:gsub("\r\n", "\n") -- Canonicalise the string, making it valid UTF-8. data = CanonicaliseString(data) -- Collapse complex elements. data = data:gsub("< ?(%w+) ?[^>]*(/?)>", "<%1%2>") -- Helper function for reading tokens from the HTML stream. local pos = 1 local len = data:len() local function tokens() if (pos >= len) then return nil end local s, e, t s, e, t = string_find(data, "^([ \n])", pos) if s then pos = e+1 return t end if string_find(data, "^%c") then pos = pos + 1 return tokens() end s, e, t = string_find(data, "^(<[^>]*>)", pos) if s then pos = e+1 return t:lower() end s, e, t = string_find(data, "^(&[^;]-;)", pos) if s then pos = e+1 return t end s, e, t = string_find(data, "^([^ <&\n]+)", pos) if s then pos = e+1 return t end t = string_sub(data, pos, pos+1) pos = pos + 1 return t end -- Skip tokens until we hit a . for t in tokens do if (t == "") then break end end -- Define the element look-up table. local document = CreateDocument() local importer = CreateImporter(document) local style = "P" local pre = false local function flush() importer:flushparagraph(style) style = "P" end local function flushword() importer:flushword(pre) end local function flushpre() flush() if pre then style = "PRE" end end local elements = { [" "] = flushword, ["

    "] = flush, ["
    "] = flushpre, ["
    "] = flushpre, ["

    "] = flush, [""] = flush, [""] = flush, [""] = flush, ["

    "] = function() flush() style = "H1" end, ["

    "] = function() flush() style = "H2" end, ["

    "] = function() flush() style = "H3" end, ["

    "] = function() flush() style = "H4" end, ["
  • "] = function() flush() style = "LB" end, [""] = function() importer:style_on(ITALIC) end, [""] = function() importer:style_off(ITALIC) end, [""] = function() importer:style_on(ITALIC) end, [""] = function() importer:style_off(ITALIC) end, [""] = function() importer:style_on(UNDERLINE) end, [""] = function() importer:style_off(UNDERLINE) end, [""] = function() importer:style_on(BOLD) end, [""] = function() importer:style_off(BOLD) end, ["
    "] = function() flush() style = "PRE" pre = true end,
    		["
    "] = function() flush() pre = false end, ["\n"] = function() if pre then flush() style = "PRE" else flushword() end end } -- Actually do the parsing. importer:reset() for t in tokens do local e = elements[t] if e then e() elseif string_find(t, "^<") then -- do nothing elseif string_find(t, "^&") then e = DecodeHTMLEntity(t) if e then importer:text(e) end else importer:text(t) end end flush() return document end function Cmd.ImportHTMLFile(filename) return ImportFileWithUI(filename, "Import HTML File", loadhtmlfile) end wordgrinder-0.5.1.orig/src/lua/import/opendocument.lua0000644000000000000000000001565612243251035017767 0ustar -- © 2008-2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local BOLD = wg.BOLD local ParseWord = wg.parseword local WriteU8 = wg.writeu8 local ReadFromZip = wg.readfromzip local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local string_char = string.char local string_find = string.find local string_sub = string.sub local string_gmatch = string.gmatch local table_concat = table.concat local OFFICE_NS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0" local STYLE_NS = "urn:oasis:names:tc:opendocument:xmlns:style:1.0" local FO_NS = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" local TEXT_NS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0" ----------------------------------------------------------------------------- -- The importer itself. local function parse_style(styles, xml) local NAME = STYLE_NS .. " name" local FAMILY = STYLE_NS .. " family" local PARENT_NAME = STYLE_NS .. " parent-name" local TEXT_PROPERTIES = STYLE_NS .. " text-properties" local PARAGRAPH_PROPERTIES = STYLE_NS .. " paragraph-properties" local FONT_STYLE = FO_NS .. " font-style" local FONT_WEIGHT = FO_NS .. " font-weight" local UNDERLINE_STYLE = STYLE_NS .. " text-underline-style" local MARGIN_LEFT = FO_NS .. " margin-left" local name = xml[NAME] local style = { parent = xml[PARENT_NAME] } for _, element in ipairs(xml) do if (element._name == TEXT_PROPERTIES) then if (element[FONT_STYLE] == "italic") then style.italic = true end if (element[FONT_WEIGHT] == "bold") then style.bold = true end if (element[UNDERLINE_STYLE] == "solid") then style.underline = true end elseif (element._name == PARAGRAPH_PROPERTIES) then if element[MARGIN_LEFT] then style.indented = true end end end styles[name] = style end local function resolve_parent_styles(styles) local function recursively_fetch(name, attr) local style = styles[name] if style[attr] then return true end if style.parent then return recursively_fetch(style.parent, attr) end return nil end for k, v in pairs(styles) do v.italic = recursively_fetch(k, "italic") v.bold = recursively_fetch(k, "bold") v.underline = recursively_fetch(k, "underline") v.indented = recursively_fetch(k, "indented") end end local function collect_styles(styles, xml) local STYLES = OFFICE_NS .. " styles" local AUTOMATIC_STYLES = OFFICE_NS .. " automatic-styles" local STYLE = STYLE_NS .. " style" for _, element in ipairs(xml) do if (element._name == STYLES) or (element._name == AUTOMATIC_STYLES) then for _, element in ipairs(element) do if (element._name == STYLE) then parse_style(styles, element) end end end end end local function add_text(styles, importer, xml) local SPACE = TEXT_NS .. " s" local SPACECOUNT = TEXT_NS .. " c" local SPAN = TEXT_NS .. " span" local STYLENAME = TEXT_NS .. " style-name" for _, element in ipairs(xml) do if (type(element) == "string") then local needsflush = false if string_find(element, "^ ") then needsflush = true end for word in string_gmatch(element, "%S+") do if needsflush then importer:flushword(false) end importer:text(word) needsflush = true end if string_find(element, " $") then importer:flushword(false) end elseif (element._name == SPACE) then local count = tonumber(element[SPACECOUNT] or 0) + 1 for i = 1, count do importer:flushword(false) end elseif (element._name == SPAN) then local stylename = element[STYLENAME] or "" local style = styles[stylename] or {} if style.italic then importer:style_on(ITALIC) end if style.bold then importer:style_on(BOLD) end if style.underline then importer:style_on(UNDERLINE) end add_text(styles, importer, element) if style.underline then importer:style_off(UNDERLINE) end if style.bold then importer:style_off(BOLD) end if style.italic then importer:style_off(ITALIC) end else add_text(styles, importer, element) end end end local function import_paragraphs(styles, importer, xml, defaultstyle) local PARAGRAPH = TEXT_NS .. " p" local HEADER = TEXT_NS .. " h" local LIST = TEXT_NS .. " list" local OUTLINELEVEL = TEXT_NS .. " outline-level" local STYLENAME = TEXT_NS .. " style-name" for _, element in ipairs(xml) do if (element._name == PARAGRAPH) then local stylename = element[STYLENAME] or "" local style = styles[stylename] or {} local wgstyle = defaultstyle if style.indented then wgstyle = "Q" end add_text(styles, importer, element) importer:flushparagraph(wgstyle) elseif (element._name == HEADER) then local level = tonumber(element[OUTLINELEVEL] or 1) if (level > 4) then level = 4 end add_text(styles, importer, element) importer:flushparagraph("H"..level) elseif (element._name == LIST) then for _, element in ipairs(element) do import_paragraphs(styles, importer, element, "LB") end end end end function Cmd.ImportODTFile(filename) if not filename then filename = FileBrowser("Import ODT File", "Import from:", false) if not filename then return false end end ImmediateMessage("Importing...") -- Load the styles and content subdocuments. local stylesxml = ReadFromZip(filename, "styles.xml") local contentxml = ReadFromZip(filename, "content.xml") if not stylesxml or not contentxml then ModalMessage(nil, "The import failed, probably because the file could not be found.") QueueRedraw() return false end stylesxml = ParseXML(stylesxml) contentxml = ParseXML(contentxml) -- Find out what text styles the document creates (so we can identify -- italic and underlined text). local styles = {} collect_styles(styles, stylesxml) collect_styles(styles, contentxml) resolve_parent_styles(styles) -- Actually import the content. local document = CreateDocument() local importer = CreateImporter(document) importer:reset() local BODY = OFFICE_NS .. " body" local TEXT = OFFICE_NS .. " text" for _, element in ipairs(contentxml) do if (element._name == BODY) then for _, element in ipairs(element) do if (element._name == TEXT) then import_paragraphs(styles, importer, element, "P") end end end end -- All the importers produce a blank line at the beginning of the -- document (the default content made by CreateDocument()). Remove it. if (#document > 1) then document:deleteParagraphAt(1) end -- Add the document to the document set. local docname = Leafname(filename) if DocumentSet.documents[docname] then local id = 1 while true do local f = docname.."-"..id if not DocumentSet.documents[f] then docname = f break end end end DocumentSet:addDocument(document, docname) DocumentSet:setCurrent(docname) QueueRedraw() return true end wordgrinder-0.5.1.orig/src/lua/import/text.lua0000644000000000000000000000174112123321132016232 0ustar -- © 2008-2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local ParseWord = wg.parseword local WriteU8 = wg.writeu8 local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local string_char = string.char local string_find = string.find local string_sub = string.sub local table_concat = table.concat ----------------------------------------------------------------------------- -- The importer itself. local function loadtextfile(fp) local document = CreateDocument() for l in fp:lines() do l = CanonicaliseString(l) l = l:gsub("%c+", "") local p = CreateParagraph(DocumentSet.styles["P"], ParseStringIntoWords(l)) document:appendParagraph(p) end return document end function Cmd.ImportTextFile(filename) return ImportFileWithUI(filename, "Import Text File", loadtextfile) end wordgrinder-0.5.1.orig/src/lua/import.lua0000644000000000000000000000557612123320200015253 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local ITALIC = wg.ITALIC local UNDERLINE = wg.UNDERLINE local ParseWord = wg.parseword local WriteU8 = wg.writeu8 local bitand = bit32.band local bitor = bit32.bor local bitxor = bit32.bxor local bit = bit32.btest local string_char = string.char local string_find = string.find local string_sub = string.sub local table_concat = table.concat function ParseStringIntoWords(s) local words = {} for w in s:gmatch("[^ \t\r\n]+") do words[#words + 1] = CreateWord(w) end if (#words == 0) then return {CreateWord()} end return words end -- Import helper functions. These functions build styled words and paragraphs. function CreateImporter(document) local pbuffer local wbuffer local oldattr local attr return { reset = function(self) pbuffer = {} wbuffer = {} oldattr = 0 attr = 0 end, style_on = function(self, a) attr = bitor(attr, a) end, style_off = function(self, a) attr = bitxor(bitor(attr, a), a) end, text = function(self, t) if (oldattr ~= attr) then wbuffer[#wbuffer + 1] = string_char(16 + attr) oldattr = attr end wbuffer[#wbuffer + 1] = t end, flushword = function(self, force) if (#wbuffer > 0) or force then local s = table_concat(wbuffer) pbuffer[#pbuffer + 1] = CreateWord(s) wbuffer = {} oldattr = 0 end end, flushparagraph = function(self, style) style = style or "P" if (#wbuffer > 0) then self:flushword() end if (#pbuffer > 0) then local p = CreateParagraph(DocumentSet.styles[style], pbuffer) document:appendParagraph(p) pbuffer = {} end end } end -- Does the standard selector-box-and-progress UI for each importer. function ImportFileWithUI(filename, title, callback) if not filename then filename = FileBrowser(title, "Import from:", false) if not filename then return false end end ImmediateMessage("Importing...") -- Actually import the file. local fp = io.open(filename) if not fp then return nil end local document = callback(fp) if not document then ModalMessage(nil, "The import failed, probably because the file could not be found.") QueueRedraw() return false end fp:close() -- All the importers produce a blank line at the beginning of the -- document (the default content made by CreateDocument()). Remove it. if (#document > 1) then document:deleteParagraphAt(1) end -- Add the document to the document set. local docname = Leafname(filename) if DocumentSet.documents[docname] then local id = 1 while true do local f = docname.."-"..id if not DocumentSet.documents[f] then docname = f break end end end DocumentSet:addDocument(document, docname) DocumentSet:setCurrent(docname) QueueRedraw() return true end wordgrinder-0.5.1.orig/src/lua/main.lua0000644000000000000000000001447012243251035014672 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local Write = wg.write local SetNormal = wg.setnormal local SetBold = wg.setbold local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local GetStringWidth = wg.getstringwidth local redrawpending = true -- Determine the user's home directory. HOME = os.getenv("HOME") or os.getenv("USERPROFILE") function QueueRedraw() redrawpending = true if not Document.wrapwidth then ResizeScreen() end end function ResetDocumentSet() DocumentSet = CreateDocumentSet() DocumentSet.menu = CreateMenu() DocumentSet:addDocument(CreateDocument(), "main") DocumentSet:setCurrent("main") RebuildParagraphStylesMenu(DocumentSet.styles) RebuildDocumentsMenu(DocumentSet.documents) DocumentSet:purge() DocumentSet:clean() FireEvent(Event.DocumentCreated) FireEvent(Event.RegisterAddons) end -- This function contains the word processor proper, including the main event -- loop. function WordProcessor(filename) wg.initscreen() ResizeScreen() RedrawScreen() if filename then Cmd.LoadDocumentSet(filename) end --ModalMessage("Welcome!", "Welcome to WordGrinder! While editing, you may press ESC for the menu, or ESC, F, X to exit (or ALT+F, X if your terminal supports it).") local masterkeymap = { ["KEY_RESIZE"] = function() -- resize ResizeScreen() RedrawScreen() end, ["KEY_REDRAW"] = RedrawScreen, [" "] = Cmd.SplitCurrentWord, ["KEY_RETURN"] = Cmd.SplitCurrentParagraph, ["KEY_ESCAPE"] = Cmd.ActivateMenu, } local function eventloop() local nl = string.char(13) while true do if DocumentSet.justchanged then FireEvent(Event.Changed) DocumentSet.justchanged = false end FireEvent(Event.WaitingForUser) local c = "KEY_TIMEOUT" while (c == "KEY_TIMEOUT") do if redrawpending then RedrawScreen() redrawpending = false end c = wg.getchar(DocumentSet.idletime) if (c == "KEY_TIMEOUT") then FireEvent(Event.Idle) end end ResetNonmodalMessages() -- Anything in masterkeymap overrides everything else. local f = masterkeymap[c] if f then f() else -- It's not in masterkeymap. If it's printable, insert it; if it's -- not, look it up in the menu hierarchy. if not c:match("^KEY_") then Cmd.InsertStringIntoWord(c) else f = DocumentSet.menu:lookupAccelerator(c) if f then if (type(f) == "function") then f() else Cmd.ActivateMenu(f) end else NonmodalMessage(c:gsub("^KEY_", "").." is not bound --- try ESCAPE for a menu") end end end end end while true do local f, e = xpcall(eventloop, Traceback) if not f then ModalMessage("Internal error!", "Something went wrong inside WordGrinder! I'll try and ".. "continue but you should save your work immediately (under a ".. "different filename), exit, and send the following technical ".. "information to the author:\n\n" .. e) end end end -- Program entry point. Parses command line arguments and executes. function Main(...) -- Set up the initial document so that the command line options have -- access. ResetDocumentSet() local filename = nil do local stdout = io.stdout local stderr = io.stderr local function message(...) stderr:write("wordgrinder: ", ...) stderr:write("\n") end local function usererror(...) message(...) os.exit(1) end local function do_help(opt) stdout:write("WordGrinder version ", VERSION, " © 2007-2008 David Given\n") if DEBUG then stdout:write("(This version has been compiled with debugging enabled.)\n") end stdout:write([[ Syntax: wordgrinder [] [] Options: -h --help Displays this message. --lua file.lua Loads and executes file.lua before startup -c --convert src dest Converts from one file format to another Only one filename may be specified, which is the name of a WordGrinder file to load on startup. If not given, you get a blank document instead. To convert documents, use --convert. The file type is autodetected from the extension. To specify a document name, use :name as a suffix. e.g.: wordgrinder --convert filename.wg:"Chapter 1" chapter1.odt ]]) if DEBUG then -- List debugging options here. end os.exit(0) end local function do_lua(opt1, opt2) if not opt1 then usererror("--lua must have an argument") end local f, e = loadfile(opt1) if e then usererror("user script compilation error: "..e) end return f(opt2) or 1 end local function do_convert(opt1, opt2) if not opt1 or not opt2 then usererror("--convert must have two arguments") end CliConvert(opt1, opt2) end local function needarg(opt) if not opt then usererror("missing option parameter") end end local argmap = { ["h"] = do_help, ["help"] = do_help, ["lua"] = do_lua, ["c"] = do_convert, ["convert"] = do_convert, } if DEBUG then -- argmap["p"] = do_profile -- argmap["profile"] = do_profile end -- Called on an unrecognised option. local function unrecognisedarg(arg) usererror("unrecognised option '", arg, "' --- try --help for help") end -- Do the actual argument parsing. local arg = {...} local i = 2 while (i <= #arg) do local o = arg[i] local op if (o:byte(1) == 45) then -- This is an option. if (o:byte(2) == 45) then -- ...with a -- prefix. o = o:sub(3) local fn = argmap[o] if not fn then unrecognisedarg("--"..o) end i = i + fn(arg[i+1], arg[i+2]) else -- ...without a -- prefix. local od = o:sub(2, 2) local fn = argmap[od] if not fn then unrecognisedarg("-"..od) end op = o:sub(3) if (op == "") then i = i + fn(arg[i+1], arg[i+2]) else fn(op) end end else if filename then usererror("you may only specify one filename") end filename = o end i = i + 1 end end if filename and not filename:find("^/") and not filename:find("^[a-zA-Z]:[/\\]") then filename = lfs.currentdir() .. "/" .. filename end WordProcessor(filename) end wordgrinder-0.5.1.orig/src/lua/margin.lua0000644000000000000000000000435511523520773015234 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local GetStringWidth = wg.getstringwidth -- This code defines the various controllers that work the margin displays. -- It's all a little overengineered, but is really intended to test some -- modularisation concepts. local no_margin_controller = { attach = function(self) Document.margin = 0 NonmodalMessage("Hiding margin.") end, getcontent = function(self, pn, paragraph) return nil end } local style_name_controller = { attach = function(self) local m = 0 for _, style in pairs(DocumentSet.styles) do local mm = GetStringWidth(style.name) if (mm > m) then m = mm end end Document.margin = m + 1 NonmodalMessage("Margin now displays paragraph styles.") end, getcontent = function(self, pn, paragraph) return paragraph.style.name end } local paragraph_number_controller = { attach = function(self) local cb = function() Document.margin = int(math.log10(#Document)) + 3 end self.token = AddEventListener(Event.Changed, cb) cb() NonmodalMessage("Margin now displays paragraph numbers.") end, detach = function(self) RemoveEventListener(self.token) self.token = nil end, getcontent = function(self, pn, paragraph) return tostring(pn) end } local word_count_controller = { attach = function(self) Document.margin = 5 NonmodalMessage("Margin now displays word counts.") end, getcontent = function(self, pn, paragraph) return tostring(#paragraph) end } MarginControllers = { [1] = no_margin_controller, [2] = style_name_controller, [3] = paragraph_number_controller, [4] = word_count_controller } --- Sets a specific margin mode for the current document. -- -- @param mode the new margin mode function SetMarginMode(mode) local controller = MarginControllers[Document.viewmode] if controller.detach then controller:detach() end Document.viewmode = mode controller = MarginControllers[Document.viewmode] if controller.attach then controller:attach() end DocumentSet:touch() ResizeScreen() end function Cmd.SetViewMode(mode) SetMarginMode(mode) QueueRedraw() return true end wordgrinder-0.5.1.orig/src/lua/menu.lua0000644000000000000000000003264612247114240014717 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local Write = wg.write local ClearToEOL = wg.cleartoeol local GetChar = wg.getchar local GotoXY = wg.gotoxy local SetBold = wg.setbold local SetBright = wg.setbright local SetReverse = wg.setreverse local SetNormal = wg.setnormal local GetStringWidth = wg.getstringwidth local menu_tab = {} local key_tab = {} local menu_stack = {} local function addmenu(n, m, menu) local w = n:len() menu = menu or {} menu.label = n menu.mks = {} for i, _ in ipairs(menu) do menu[i] = nil end for _, data in ipairs(m) do if (data == "-") then menu[#menu+1] = "-" else local item = { id = data[1], mk = data[2], label = data[3], ak = data[4], fn = data[5] } menu[#menu+1] = item if item.mk then if menu.mks[item.mk] then error("Duplicate menu action key "..item.mk) end menu.mks[item.mk] = item end if menu_tab[item.id] then error("Dupicate menu ID "..item.id) end menu_tab[item.id] = item if item.ak then key_tab[item.ak] = item.id end if (item.label:len() > w) then w = item.label:len() end end end menu.maxwidth = w return menu end local function submenu(menu) for _, item in ipairs(menu) do menu_tab[item.id] = nil if item.ak then key_tab[item.ak] = nil end end end local DocumentsMenu = addmenu("Documents", {}) local ParagraphStylesMenu = addmenu("Paragraph Styles", {}) local ImportMenu = addmenu("Import new document", { {"FIodt", "O", "Import ODT file...", nil, Cmd.ImportODTFile}, {"FIhtml", "H", "Import HTML file...", nil, Cmd.ImportHTMLFile}, {"FItxt", "T", "Import text file...", nil, Cmd.ImportTextFile}, }) local ExportMenu = addmenu("Export current document", { {"FEodt", "O", "Export to ODT...", nil, Cmd.ExportODTFile}, {"FEhtml", "H", "Export to HTML...", nil, Cmd.ExportHTMLFile}, {"FEtxt", "T", "Export to plain text...", nil, Cmd.ExportTextFile}, {"FEtex", "L", "Export to LaTeX...", nil, Cmd.ExportLatexFile}, {"FEtr", "F", "Export to Troff...", nil, Cmd.ExportTroffFile}, -- {"FErtf", "R", "Export to Rtf...", nil, Cmd.ExportRTFFile}, }) local SettingsMenu = addmenu("Settings", { {"FSautosave", "A", "Autosave...", nil, Cmd.ConfigureAutosave}, {"FSscrapbook", "S", "Scrapbook...", nil, Cmd.ConfigureScrapbook}, {"FSHTMLExport", "H", "HTML export...", nil, Cmd.ConfigureHTMLExport}, {"FSPageCount", "P", "Page count...", nil, Cmd.ConfigurePageCount}, {"FSWidescreen", "W", "Widescreen mode...", nil, Cmd.ConfigureWidescreen},ConfigureWidescreen }) local FileMenu = addmenu("File", { {"FN", "N", "New document set", nil, Cmd.CreateBlankDocumentSet}, {"FO", "O", "Load document set...", nil, Cmd.LoadDocumentSet}, {"FS", "S", "Save document set", "^S", Cmd.SaveCurrentDocument}, {"FA", "A", "Save document set as...", nil, Cmd.SaveCurrentDocumentAs}, "-", {"FB", "B", "Add new blank document", nil, Cmd.AddBlankDocument}, {"FI", "I", "Import new document ▷", nil, ImportMenu}, {"FE", "E", "Export current document ▷", nil, ExportMenu}, {"Fdocman", "D", "Manage documents...", nil, Cmd.ManageDocumentsUI}, "-", {"Fsettings", "T", "Settings ▷", nil, SettingsMenu}, "-", {"Fabout", "Z", "About WordGrinder...", nil, Cmd.AboutWordGrinder}, {"FQ", "X", "Exit", "^Q", Cmd.TerminateProgram} }) local ScrapbookMenu = addmenu("Scrapbook", { {"EScut", "T", "Cut to scrapbook", nil, Cmd.CutToScrapbook}, {"EScopy", "C", "Copy to scrapbook", nil, Cmd.CopyToScrapbook}, {"ESpaste", "P", "Paste to scrapbook", nil, Cmd.PasteToScrapbook}, }) local EditMenu = addmenu("Edit", { {"ET", "T", "Cut", "^X", Cmd.Cut}, {"EC", "C", "Copy", "^C", Cmd.Copy}, {"EP", "P", "Paste", "^V", Cmd.Paste}, {"ED", "D", "Delete", nil, Cmd.Delete}, "-", {"EF", "F", "Find and replace...", "^F", Cmd.Find}, {"EN", "N", "Find next", "^K", Cmd.FindNext}, {"ER", "R", "Replace then find", "^R", Cmd.ReplaceThenFind}, "-", {"EG", "G", "Go to...", "^G", Cmd.Goto}, {"Escrapbook", "S", "Scrapbook ▷", nil, ScrapbookMenu}, }) local MarginMenu = addmenu("Margin", { {"SM1", "H", "Hide margin", nil, function() Cmd.SetViewMode(1) end}, {"SM2", "S", "Show paragraph styles", nil, function() Cmd.SetViewMode(2) end}, {"SM3", "N", "Show paragraph numbers", nil, function() Cmd.SetViewMode(3) end}, {"SM4", "W", "Show paragraph word counts", nil, function() Cmd.SetViewMode(4) end}, }) local StyleMenu = addmenu("Style", { {"SI", "I", "Set italic", "^I", function() Cmd.ToggleStyle("i") end}, {"SU", "U", "Set underline", "^U", function() Cmd.ToggleStyle("u") end}, {"SB", "B", "Set bold", "^B", function() Cmd.ToggleStyle("b") end}, {"SO", "O", "Set plain", "^O", function() Cmd.ToggleStyle("o") end}, "-", {"SP", "P", "Change paragraph style ▷", "^P", ParagraphStylesMenu}, {"SM", "M", "Set margin mode ▷", nil, MarginMenu}, {"SS", "S", "Toggle status bar", "^W", Cmd.ToggleStatusBar}, }) local NavigationMenu = addmenu("Navigation", { {"ZU", nil, "Cursor up", "UP", Cmd.GotoPreviousLine}, {"ZR", nil, "Cursor right", "RIGHT", Cmd.GotoNextCharW}, {"ZD", nil, "Cursor down", "DOWN", Cmd.GotoNextLine}, {"ZL", nil, "Cursor left", "LEFT", Cmd.GotoPreviousCharW}, {"ZWL", nil, "Goto previous word", "SLEFT", Cmd.GotoPreviousWordW}, {"ZWR", nil, "Goto next word", "SRIGHT", Cmd.GotoNextWordW}, {"ZNP", nil, "Goto next paragraph", "SDOWN", Cmd.GotoNextParagraphW}, {"ZPP", nil, "Goto previous paragraph", "SUP", Cmd.GotoPreviousParagraphW}, {"ZH", nil, "Goto beginning of line", "HOME", Cmd.GotoBeginningOfLine}, {"ZE", nil, "Goto end of line", "END", Cmd.GotoEndOfLine}, {"ZBD", nil, "Goto beginning of document", "SHOME", Cmd.GotoBeginningOfDocument}, {"ZED", nil, "Goto end of document", "SEND", Cmd.GotoEndOfDocument}, {"ZPGUP", nil, "Page up", "PGUP", Cmd.GotoPreviousPage}, {"ZPGDN", nil, "Page down", "PGDN", Cmd.GotoNextPage}, {"ZDPC", nil, "Delete previous character", "BACKSPACE", Cmd.DeletePreviousChar}, {"ZDNC", nil, "Delete next character", "DELETE", Cmd.DeleteNextChar}, {"ZM", nil, "Toggle mark", "^@", Cmd.ToggleMark}, }) local MainMenu = addmenu("Main Menu", { {"F", "F", "File ▷", nil, FileMenu}, {"E", "E", "Edit ▷", nil, EditMenu}, {"S", "S", "Style ▷", nil, StyleMenu}, {"D", "D", "Documents ▷", nil, DocumentsMenu}, {"Z", "Z", "Navigation ▷", nil, NavigationMenu} }) --- MENU DRIVER CLASS --- MenuClass = { activate = function(self, menu) menu = menu or MainMenu self:runmenu(0, 0, menu) QueueRedraw() SetNormal() end, drawmenu = function(self, x, y, menu, n) local akw = 0 for _, item in ipairs(menu) do local ak = self.accelerators[item.id] if ak then local l = GetStringWidth(ak) if (akw < l) then akw = l end end end if (akw > 0) then akw = akw + 1 end local w = menu.maxwidth + 4 + akw DrawTitledBox(x, y, w, #menu, menu.label) for i, item in ipairs(menu) do local ak = self.accelerators[item.id] if (item == "-") then if (i == n) then SetReverse() end SetBright() Write(x+1, y+i, string.rep("─", w)) else if (i == n) then SetReverse() Write(x+1, y+i, string.rep(" ", w)) end Write(x+4, y+i, item.label) SetBold() SetBright() if ak then local l = GetStringWidth(ak) Write(x+w-l, y+i, ak) end if item.mk then Write(x+2, y+i, item.mk) end end SetNormal() end GotoXY(ScreenWidth-1, ScreenHeight-1) DrawStatusLine("^V rebinds a menu item; ^X unbinds it; ^R resets all bindings to default.") end, drawmenustack = function(self) local osb = DocumentSet.statusbar DocumentSet.statusbar = true RedrawScreen() DocumentSet.statusbar = osb local o = 0 for _, menu in ipairs(menu_stack) do self:drawmenu(o*4, o*2, menu.menu, menu.n) o = o + 1 end end, runmenu = function(self, x, y, menu) local n = 1 while true do local id while true do self:drawmenu(x, y, menu, n) local c = GetChar():upper() if (c == "KEY_UP") and (n > 1) then n = n - 1 elseif (c == "KEY_DOWN") and (n < #menu) then n = n + 1 elseif (c == "KEY_RETURN") or (c == "KEY_RIGHT") then if (type(menu[n]) ~= "string") then id = menu[n].id break end elseif (c == "KEY_LEFT") then return nil elseif (c == "KEY_ESCAPE") then return false elseif (c == "KEY_^C") then return false elseif (c == "KEY_^X") then local item = menu[n] if (type(item) ~= "string") then local ak = self.accelerators[item.id] if ak then self.accelerators[ak] = nil self.accelerators[item.id] = nil self:drawmenustack() end end elseif (c == "KEY_^V") then local item = menu[n] if (type(item) ~= "string") and not self.accelerators[item.id] then DrawStatusLine("Press new accelerator key for menu item.") local ak = GetChar():upper() if ak:match("^KEY_") then ak = ak:gsub("^KEY_", "") if self.accelerators[ak] then NonmodalMessage("Sorry, "..ak.." is already bound elsewhere.") elseif (ak == "ESCAPE") or (ak == "RESIZE") then NonmodalMessage("You can't bind that key.") else self.accelerators[ak] = item.id self.accelerators[item.id] = ak end self:drawmenustack() end end elseif (c == "KEY_^R") then if PromptForYesNo("Reset menu keybindings?", "Are you sure you want to reset all the menu ".. "keybindings back to their defaults?") then DocumentSet.menu = CreateMenu() DocumentSet:touch() NonmodalMessage("All keybindings have been reset to their default settings.") menu_stack = {} return false end self:drawmenustack() elseif menu.mks[c] then id = menu.mks[c].id break end end local item = menu_tab[id] local f = item.fn if (type(f) == "table") then menu_stack[#menu_stack+1] = { menu = menu, n = n } local r = self:runmenu(x+4, y+2, f) menu_stack[#menu_stack] = nil if (r == true) then return true elseif (r == false) then return false end self:drawmenustack() else if not f then ModalMessage("Not implemented yet", "Sorry, that feature isn't implemented yet. (This should never happen. Complain.)") else local _, msg = f() if msg then NonmodalMessage(msg) end end menu_stack = {} return true end end end, lookupAccelerator = function(self, c) c = c:gsub("^KEY_", "") local id = self.accelerators[c] if not id then return nil end local item = menu_tab[id] local f if not item then f = function() NonmodalMessage("Fnord: menu ID "..id.." not found.") end else f = item.fn end return f end } function CreateMenu() local my_key_tab = {} for ak, id in pairs(key_tab) do my_key_tab[ak] = id my_key_tab[id] = ak end local m = { accelerators = my_key_tab } setmetatable(m, {__index = MenuClass}) return m end function RebuildParagraphStylesMenu(styles) submenu(ParagraphStylesMenu) local m = {} for id, style in ipairs(styles) do local shortcut if (id <= 10) then shortcut = tostring(id - 1) else shortcut = string.char(id + 54) end m[#m+1] = {"SP"..id, shortcut, style.name..": "..style.desc, nil, function() Cmd.ChangeParagraphStyle(style.name) end} end addmenu("Paragraph Styles", m, ParagraphStylesMenu) end function RebuildDocumentsMenu(documents) -- Remember any accelerator keys and unhook the old menu. local ak_tab = {} for _, item in ipairs(DocumentsMenu) do local ak = DocumentSet.menu.accelerators[item.id] if ak then ak_tab[item.label] = ak end end submenu(DocumentsMenu) -- Construct the new menu. local m = {} for id, document in ipairs(documents) do local ak = ak_tab[document.name] local shortcut if (id <= 10) then shortcut = tostring(id - 1) else shortcut = string.char(id + 54) end m[#m+1] = {"D"..id, shortcut, document.name, ak, function() Cmd.ChangeDocument(document.name) end} end -- Hook it. addmenu("Documents", m, DocumentsMenu) end wordgrinder-0.5.1.orig/src/lua/navigate.lua0000644000000000000000000004430012243251035015537 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local GetBytesOfCharacter = wg.getbytesofcharacter local GetStringWidth = wg.getstringwidth local NextCharInWord = wg.nextcharinword local PrevCharInWord = wg.prevcharinword local InsertIntoWord = wg.insertintoword local DeleteFromWord = wg.deletefromword local ApplyStyleToWord = wg.applystyletoword local ReadU8 = wg.readu8 local WriteU8 = wg.writeu8 local table_concat = table.concat function Cmd.GotoBeginningOfWord() Document.co = 1 QueueRedraw() return true end function Cmd.GotoEndOfWord() Document.co = Document[Document.cp][Document.cw].text:len() + 1 QueueRedraw() return true end function Cmd.GotoBeginningOfParagraph() Document.cw = 1 return Cmd.GotoBeginningOfWord() end function Cmd.GotoEndOfParagraph() Document.cw = #Document[Document.cp] return Cmd.GotoEndOfWord() end function Cmd.GotoBeginningOfDocument() Document.cp = 1 return Cmd.GotoBeginningOfParagraph() end function Cmd.GotoEndOfDocument() Document.cp = #Document return Cmd.GotoEndOfParagraph() end function Cmd.GotoPreviousParagraph() if (Document.cp == 1) then QueueRedraw() return false end Document.cp = Document.cp - 1 Document.cw = 1 Document.co = 1 QueueRedraw() return true end function Cmd.GotoNextParagraph() if (Document.cp == #Document) then QueueRedraw() return false end Document.cp = Document.cp + 1 Document.cw = 1 Document.co = 1 QueueRedraw() return true end function Cmd.GotoPreviousParagraphW() return Cmd.GotoPreviousParagraph() end function Cmd.GotoNextParagraphW() return Cmd.GotoNextParagraph() end function Cmd.GotoPreviousWord() if (Document.cw == 1) then QueueRedraw() return false end Document.cw = Document.cw - 1 Document.co = 1 QueueRedraw() return true end function Cmd.GotoNextWord() local p = Document[Document.cp] if (Document.cw == #p) then QueueRedraw() return false end Document.cw = Document.cw + 1 Document.co = 1 QueueRedraw() return true end function Cmd.GotoPreviousWordW() if not Cmd.GotoPreviousWord() then return Cmd.GotoPreviousParagraphW() and Cmd.GotoEndOfParagraph() end return true end function Cmd.GotoNextWordW() if not Cmd.GotoNextWord() then return Cmd.GotoNextParagraphW() and Cmd.GotoBeginningOfParagraph() end return true end function Cmd.GotoPreviousChar() local word = Document[Document.cp][Document.cw] local co = PrevCharInWord(word.text, Document.co) if not co then return false end Document.co = co QueueRedraw() return true end function Cmd.GotoNextChar() local word = Document[Document.cp][Document.cw] local co = NextCharInWord(word.text, Document.co) if not co then return false end Document.co = co QueueRedraw() return true end function Cmd.GotoPreviousCharW() if not Cmd.GotoPreviousChar() then return Cmd.GotoPreviousWordW() and Cmd.GotoEndOfWord() end return true end function Cmd.GotoNextCharW() if not Cmd.GotoNextChar() then return Cmd.GotoNextWordW() and Cmd.GotoBeginningOfWord() end return true end function Cmd.InsertStringIntoWord(c) local paragraph = Document[Document.cp] local word = paragraph[Document.cw] local s, co = InsertIntoWord(word.text, c, Document.co) if not co then return false end word.text = s Document.co = co paragraph:touch() DocumentSet:touch() QueueRedraw() return true end function Cmd.InsertStringIntoParagraph(c) local first = true for word in c:gmatch("[^%s]+") do if not first then Cmd.SplitCurrentWord() end Cmd.InsertStringIntoWord(word) first = false end return true end function Cmd.SplitCurrentWord() local paragraph = Document[Document.cp] local word = paragraph[Document.cw] local text = word.text local co = Document.co local left = DeleteFromWord(text, co, text:len()+1) local newword = CreateWord(left) word.text = DeleteFromWord(text, 1, co) paragraph:insertWordBefore(Document.cw, newword) Document.cw = Document.cw + 1 Document.co = 1 DocumentSet:touch() QueueRedraw() return true end function Cmd.JoinWithNextParagraph() if (Document.cp == #Document) then return false end Document[Document.cp]:appendWords(Document[Document.cp+1]) Document:deleteParagraphAt(Document.cp+1) DocumentSet:touch() QueueRedraw() return true end function Cmd.JoinWithNextWord() local paragraph = Document[Document.cp] local word = paragraph[Document.cw] if (Document.cw == #paragraph) then if not Cmd.JoinWithNextParagraph() then return false end end if (Document.cw ~= #paragraph) then word.text = InsertIntoWord(paragraph[Document.cw+1].text, word.text, 1) paragraph:deleteWordAt(Document.cw+1) end DocumentSet:touch() QueueRedraw() return true end function Cmd.DeletePreviousChar() return Cmd.GotoPreviousCharW() and Cmd.DeleteNextChar() end function Cmd.DeleteNextChar() local paragraph = Document[Document.cp] local word = paragraph[Document.cw] local co = Document.co local nextco = NextCharInWord(word.text, co) if not nextco then return Cmd.JoinWithNextWord() end word.text = DeleteFromWord(word.text, co, nextco) paragraph:touch() DocumentSet:touch() QueueRedraw() return true end function Cmd.SplitCurrentParagraph() Cmd.SplitCurrentWord() local p1, p2 = Document[Document.cp]:split(Document.cw) Document:deleteParagraphAt(Document.cp) Document:insertParagraphBefore(p2, Document.cp) Document:insertParagraphBefore(p1, Document.cp) Document.cp = Document.cp + 1 Document.cw = 1 Document.co = 1 return true end function Cmd.GotoXPosition(pos) local paragraph = Document[Document.cp] local lines = paragraph:wrap() local ln = paragraph:getLineOfWord(Document.cw) local l = lines[ln] local wn = #l local word pos = pos - (paragraph.style.indent or 0) if (pos < 0) then pos = 0 end while (wn > 0) do word = l[wn] if (word.x <= pos) then break end wn = wn - 1 end if (wn == 0) then wn = 1 end wo = word:getByteOfChar(pos - word.x) Document.cw = paragraph:getWordOfLine(ln) + wn - 1 Document.co = wo QueueRedraw() return false end local function getpos() local paragraph = Document[Document.cp] local lines = paragraph:wrap() local cw = Document.cw local word = paragraph[cw] local x, ln, wn = paragraph:getXOffsetOfWord(cw) x = x + word:getXOffsetOfChar(Document.co) x = x + (paragraph.style.indent or 0) return x, ln, lines end function Cmd.GotoNextLine() local x, ln, lines = getpos() if (ln == #lines) then return Cmd.GotoNextParagraph() and Cmd.GotoBeginningOfParagraph() and Cmd.GotoXPosition(x) end Document.cw = Document[Document.cp]:getWordOfLine(ln + 1) return Cmd.GotoXPosition(x) end function Cmd.GotoPreviousLine() local x, ln, lines = getpos() if (ln == 1) then return Cmd.GotoPreviousParagraph() and Cmd.GotoEndOfParagraph() and Cmd.GotoXPosition(x) end Document.cw = Document[Document.cp]:getWordOfLine(ln - 1) return Cmd.GotoXPosition(x) end function Cmd.GotoBeginningOfLine() return Cmd.GotoXPosition(0) end function Cmd.GotoEndOfLine() return Cmd.GotoXPosition(ScreenWidth) end function Cmd.GotoPreviousPage() if Document.topp and Document.topw then local x, _, _ = getpos() Document.cp = Document.topp Document.cw = Document.topw Document.co = 1 return Cmd.GotoXPosition(x) end return false end function Cmd.GotoNextPage() if Document.botp and Document.botw then local x, _, _ = getpos() Document.cp = Document.botp Document.cw = Document.botw Document.co = 1 return Cmd.GotoXPosition(x) end return false end local style_tab = { ["b"] = {wg.BOLD, 15}, ["u"] = {wg.UNDERLINE, 15}, ["i"] = {wg.ITALIC, 15}, ["o"] = {0, 0}, } function Cmd.ToggleStyle(s) if not Document.mp then return false end local mp1, mw1, mo1, mp2, mw2, mo2 = Document:getMarks() local cp, cw, co = Document.cp, Document.cw, Document.co local sor, sand = unpack(style_tab[s]) for p = mp1, mp2 do local paragraph = Document[p] local firstword = 1 local lastword = #paragraph if (p == mp1) then firstword = mw1 end if (p == mp2) then lastword = mw2 end for w = firstword, lastword do local word = paragraph[w] local fo = 1 local lo = word.text:len() + 1 if (p == mp1) and (w == mw1) then fo = mo1 end if (p == mp2) and (w == mw2) then lo = mo2 end if (p == cp) and (w == cw) then word.text, Document.co = ApplyStyleToWord(word.text, sor, sand, fo, lo, co) else word.text = ApplyStyleToWord(word.text, sor, sand, fo, lo, 0) end end paragraph:touch() end Cmd.UnsetMark() DocumentSet:touch() QueueRedraw() return true end function Cmd.ActivateMenu(menu) DocumentSet.menu:activate(menu) ResizeScreen() QueueRedraw() return true end --- Checks that it's all right to erase the current document set. -- If the current document set has unsaved modifications, asks the user -- for permission to erase them. -- -- @return true if it's all right to go ahead, false to cancel function ConfirmDocumentErasure() if DocumentSet.changed then if not PromptForYesNo("Document set not saved!", "Some of the documents in this document set contain unsaved edits. Are you sure you want to discard them, without saving first?") then return false end end return true end function Cmd.TerminateProgram() if ConfirmDocumentErasure() then os.exit() end return false end function Cmd.CreateBlankDocumentSet() if ConfirmDocumentErasure() then ResetDocumentSet() QueueRedraw() return true end return false end local function styles() local s = {} for _, style in pairs(DocumentSet.styles) do s[#s+1] = style.name .. " (HTML: " .. style.html ..")" end return s end function Cmd.ChangeParagraphStyle(style) local paragraph = Document[Document.cp] if not style then -- style = PromptForString("Change paragraph style", "Please enter the new paragraph style:", paragraph.style.name) style = Browser("Change paragraph style", "Please select the new paragraph style from the list, or enter a style name:", "Style:", styles()) end if not style then return false end style = DocumentSet.styles[style] if not style then ModalMessage("Unknown paragraph style", "Sorry! I don't recognise that style. (This user interface will be improved.)") return false end if Document.mp then local mp1, _, _, mp2, _, _ = Document:getMarks() for p = mp1, mp2 do Document[p]:changeStyle(style) end else paragraph:changeStyle(style) end DocumentSet:touch() QueueRedraw() return Cmd.UnsetMark() end function Cmd.ToggleMark() if Document.mp then Document.mp = nil Document.mw = nil Document.mo = nil else Document.mp = Document.cp Document.mw = Document.cw Document.mo = Document.co end QueueRedraw() return true end function Cmd.UnsetMark() Document.mp = nil Document.mw = nil Document.mo = nil QueueRedraw() return true end function Cmd.ChangeDocument(name) if not DocumentSet:findDocument(name) then return false end DocumentSet:setCurrent(name) QueueRedraw() return true end function Cmd.Cut() return Cmd.Copy(true) and Cmd.Delete() end function Cmd.Copy(keepselection) if not Document.mp then return false end local mp1, mw1, mo1, mp2, mw2, mo2 = Document:getMarks() local buffer = CreateDocument() DocumentSet:setClipboard(buffer) -- Copy all the paragraphs from the selected area into the clipboard. for p = mp1, mp2 do local paragraph = Document[p] buffer:appendParagraph(paragraph:copy()) end -- Remove the default content of the clipboard document. buffer:deleteParagraphAt(1) -- Remove any words in the first paragraph that weren't copied. local paragraph = buffer[1] while (mw1 > 1) do paragraph:deleteWordAt(1) mw1 = mw1 - 1 if (mp1 == mp2) then mw2 = mw2 - 1 end end -- Remove any words in the last paragraph that weren't copied. paragraph = buffer[#buffer] while (mw2 < #paragraph) do paragraph:deleteWordAt(#paragraph) end -- Remove any characters in the leading word that weren't copied. paragraph = buffer[1] word = paragraph[1] if word then word.text = DeleteFromWord(word.text, 1, mo1) if (mp1 == mp2) and (mw1 == mw2) then mo2 = mo2 - mo1 + 1 end end -- Remove any characters in the trailing word that weren't copied. paragraph = buffer[#buffer] word = paragraph[#paragraph] if word then word.text = DeleteFromWord(word.text, mo2, word.text:len()+1) end NonmodalMessage("Selected area copied to clipboard.") if not keepselection then return Cmd.UnsetMark() else return true end end function Cmd.Paste() local buffer = DocumentSet:getClipboard() if not buffer then return false end if Document.mp then if not Cmd.Delete() then return false end end -- Insert the first paragraph of the clipboard into the current paragraph. local cw = Document.cw Cmd.SplitCurrentWord() local paragraph = Document[Document.cp] for _, word in ipairs(buffer[1]) do paragraph:insertWordBefore(Document.cw, word:copy()) Document.cw = Document.cw + 1 end -- Splice the first word of the section just pasted. do local ow = Document.cw Document.cw = cw Cmd.JoinWithNextWord() Document.cw = ow - 1 end -- More than one paragraph? if (#buffer > 1) then -- Copy any remaining paragraphs in whole. Cmd.SplitCurrentParagraph() local p = 2 for p = 2, #buffer do local copy = buffer[p]:copy() Document:insertParagraphBefore(copy, Document.cp) Document.cp = Document.cp + 1 Document.cw = 1 Document.co = 1 end end -- Splice the last word of the section just pasted. NonmodalMessage("Clipboard copied to cursor position.") return Cmd.GotoPreviousCharW() and Cmd.JoinWithNextWord() end function Cmd.Delete() if not Document.mp then return false end local mp1, mw1, mo1, mp2, mw2, mo2 = Document:getMarks() -- Put the cursor at the end of the selection and split. Document.cp = mp2 Document.cw = mw2 Document.co = mo2 if not Cmd.SplitCurrentParagraph() then return false end -- Put the cursor at the beginning of the selection and split. Document.cp = mp1 Document.cw = mw1 Document.co = mo1 if not Cmd.SplitCurrentParagraph() then return false end -- We now have a whole number of paragraphs containing the area to delete. -- Delete them. for i = 1, (mp2 - mp1 + 1) do Document:deleteParagraphAt(Document.cp) end -- And merge the two areas together again. NonmodalMessage("Selected area deleted.") return Cmd.GotoPreviousCharW() and Cmd.JoinWithNextWord() and Cmd.UnsetMark() end function Cmd.Find(findtext, replacetext) if not findtext then findtext, replacetext = FindAndReplaceDialogue() if not findtext or (findtext == "") then return false end end -- Convert the search text into a pattern. local patterns = {} local words = SplitString(findtext, "%s") for _, w in ipairs(words) do -- w is a word from the pattern. We need to perform the following -- changes: -- - we need to insert %c* between all letters, to allow it to match -- control codes; -- - we want to convert single letters like 'q' into '[qQ]' to make -- the pattern case insensitive; -- - we want to anchor internal word boundaries with ^ and $ to make -- them match whole words. -- This is done in several stages, for simplicity. local wp = {} local i = 1 local len = w:len() while (i <= len) do local c = WriteU8(ReadU8(w, i)) i = i + GetBytesOfCharacter(w:byte(i)) if ((c >= "A") and (c <= "Z")) or ((c >= "a") and (c <= "z")) then c = "["..c:upper()..c:lower().."]" end wp[#wp+1] = c end patterns[#patterns + 1] = table_concat(wp, "%c*") end for i = 2, (#patterns - 1) do patterns[i] = "^"..patterns[i].."$" end if (#patterns > 1) then patterns[1] = patterns[1].."$" patterns[#patterns] = "^"..patterns[#patterns] end DocumentSet.findtext = patterns DocumentSet.replacetext = replacetext return Cmd.FindNext() end function Cmd.FindNext() if not DocumentSet.findtext then return false end ImmediateMessage("Searching...") -- Start at the current cursor position. local cp, cw, co = Document.cp, Document.cw, Document.co local patterns = DocumentSet.findtext if (#patterns == 0) then QueueRedraw() NonmodalMessage("Nothing to search for.") return false end local pattern = patterns[1] -- Keep looping until we reach the starting point again. while true do local word = Document[cp][cw] local s, e = word.text:find(pattern, co) if s then -- We got a match! First, though, check to see if the remaining -- words in the pattern match. local endword = cw local pi = 2 local found = true while (pi <= #patterns) do endword = endword + 1 word = Document[cp][endword] if not word then found = false break end _, e = word.text:find(patterns[pi]) if not e then found = false break end pi = pi + 1 end if found then Document.cp = cp Document.cw = endword Document.co = e + 1 Document.mp = Document.cp Document.mw = cw Document.mo = s NonmodalMessage("Found.") QueueRedraw() return true end end -- Nothing. Move on to the next word. co = 1 cw = cw + 1 if (cw > #Document[cp]) then cw = 1 cp = cp + 1 if (cp > #Document) then cp = 1 end end -- Check to see if we've scanned everything. if (cp == Document.cp) and (cw == Document.cw) and (co == 1) then break end end QueueRedraw() NonmodalMessage("Not found.") return false end function Cmd.ReplaceThenFind() if Document.mp then local e = Cmd.Delete() and Cmd.UnsetMark() if not e then return false end e = true local words = SplitString(DocumentSet.replacetext, "%s") for i, w in ipairs(words) do if (i > 1) then i = Cmd.SplitCurrentWord() end e = e and Cmd.InsertStringIntoWord(w) end if not e then return false end NonmodalMessage("Replaced text.") end return Cmd.FindNext() end function Cmd.ToggleStatusBar() if DocumentSet.statusbar then DocumentSet.statusbar = false NonmodalMessage("Status bar disabled.") else DocumentSet.statusbar = true NonmodalMessage("Status bar enabled.") end QueueRedraw() return true end function Cmd.AboutWordGrinder() AboutDialogue() end wordgrinder-0.5.1.orig/src/lua/redraw.lua0000644000000000000000000001447212243442530015236 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local Write = wg.write local GotoXY = wg.gotoxy local ClearArea = wg.cleararea local SetNormal = wg.setnormal local SetBold = wg.setbold local SetBright = wg.setbright local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local SetDim = wg.setdim local GetStringWidth = wg.getstringwidth local messages = {} local leftpadding = 0 function NonmodalMessage(s) messages[#messages+1] = s QueueRedraw() end function ResetNonmodalMessages() messages = {} end function ResizeScreen() ScreenWidth, ScreenHeight = wg.getscreensize() local w = GetMaximumAllowedWidth(ScreenWidth) local rw = w - Document.margin - 1 leftpadding = math.floor(ScreenWidth/2 - rw/2) Document:wrap(w - Document.margin - 1) end local function drawmargin(y, pn, p) local controller = MarginControllers[Document.viewmode] if controller.getcontent then local s = controller:getcontent(pn, p) if s then SetDim() RAlignInField(leftpadding, y, Document.margin - 1, s) SetNormal() end end local bullet = p.style.bullet if bullet then local w = GetStringWidth(bullet) + 1 local i = p.style.indent if (i >= w) then Write(leftpadding + Document.margin + i - w, y, bullet) end end end local changed_tab = { [true] = "CHANGED" } local function redrawstatus() local y = ScreenHeight - 1 if DocumentSet.statusbar then local s = { Leafname(DocumentSet.name or "(unnamed)"), "[", Document.name or "", "] ", changed_tab[DocumentSet.changed] or "", } SetReverse() ClearArea(0, ScreenHeight-1, ScreenWidth-1, ScreenHeight-1) LAlignInField(0, ScreenHeight-1, ScreenWidth, table.concat(s, "")) local s = { string.format("P: %d/%d", Document.cp, #Document), string.format("%d %s", Document.wordcount or 0, Pluralise(Document.wordcount or 0, "word", "words")) } FireEvent(Event.BuildStatusBar, s) s = table.concat(s, " │ ") if (string.sub(s, #s) == " ") then s = string.sub(s, 1, #s-1) end RAlignInField(0, ScreenHeight-1, ScreenWidth, s) SetNormal() y = y - 1 end if (#messages > 0) then SetReverse() for i = #messages, 1, -1 do ClearArea(0, y, ScreenWidth-1, y) Write(0, y, messages[i]) y = y - 1 end SetNormal() end end local topmarker = { " ▲ ▲ ▲ ▲ ▲ ", "───────────────────────────────────────────────────────" } local topmarkerwidth = GetStringWidth(topmarker[1]) local function drawtopmarker(y) local x = int((ScreenWidth - topmarkerwidth)/2) SetBright() for i = #topmarker, 1, -1 do if (y >= 0) then Write(x, y, topmarker[i]) end y = y - 1 end SetNormal() end local bottommarker = { "───────────────────────────────────────────────────────", " ▼ ▼ ▼ ▼ ▼ ", } local bottommarkerwidth = GetStringWidth(bottommarker[1]) local function drawbottommarker(y) local x = int((ScreenWidth - bottommarkerwidth)/2) SetBright() for i = 1, #bottommarker do if (y <= ScreenHeight) then Write(x, y, bottommarker[i]) end y = y + 1 end SetNormal() end function RedrawScreen() wg.clearscreen() local cp, cw, co = Document.cp, Document.cw, Document.co local cy = int(ScreenHeight / 2) local margin = Document.margin -- Find out the offset of the current paragraph. local paragraph = Document[cp] local ocw = cw cl, cw = paragraph:getLineOfWord(cw) if not cl then error("word "..ocw.." not in para of len "..#paragraph) end -- Position the cursor. do local word = paragraph[Document.cw] GotoXY(leftpadding + margin + word.x + word:getXOffsetOfChar(Document.co) + (paragraph.style.indent or 0), cy - 1) end -- Cache values for mark drawing. local mp = Document.mp local mw = Document.mw local mo = Document.mo -- Draw backwards. local pn = cp - 1 local y = cy - cl - 1 - Document:spaceAbove(cp) Document.topp = nil Document.topw = nil while (y >= 0) do local paragraph = Document[pn] if not paragraph then break end local lines = paragraph:wrap() local x = paragraph.style.indent or 0 -- FIXME for ln = #lines, 1, -1 do local l = lines[ln] if not mp then paragraph:renderLine(l, leftpadding + margin + x, y) else paragraph:renderMarkedLine(l, leftpadding + margin + x, y, nil, pn) end if (ln == 1) then drawmargin(y, pn, paragraph) end Document.topp = pn Document.topw = l.wn y = y - 1 if (y < 0) then break end end y = y - Document:spaceAbove(pn) pn = pn - 1 end if (y >= 0) then drawtopmarker(y) end -- Draw forwards. y = cy - cl pn = cp while (y < ScreenHeight) do local paragraph = Document[pn] if not paragraph then break end drawmargin(y, pn, paragraph) local x = paragraph.style.indent or 0 -- FIXME for ln, l in ipairs(paragraph:wrap()) do if not mp then paragraph:renderLine(l, leftpadding + margin + x, y) else paragraph:renderMarkedLine(l, leftpadding + margin + x, y, nil, pn) end -- If the top of the page hasn't already been set, then the -- current paragraph extends off the top of the screen. if not Document.topp and (y == 0) then Document.topp = pn Document.topw = l.wn end Document.botp = pn Document.botw = l.wn y = y + 1 if (y > ScreenHeight) then break end end y = y + Document:spaceBelow(pn) pn = pn + 1 end -- If the top of the page *still* hasn't been set, then we're on the -- first paragraph of the document. if not Document.topp then Document.topp = 1 Document.topw = 1 end if (y <= ScreenHeight) then drawbottommarker(y) end redrawstatus() FireEvent(Event.Redraw) end ----------------------------------------------------------------------------- -- Maintains the word count field in the current document. do local function cb(event, token) local wc = 0 for _, p in ipairs(Document) do wc = wc + #p end Document.wordcount = wc end AddEventListener(Event.Changed, cb) end wordgrinder-0.5.1.orig/src/lua/ui.lua0000644000000000000000000001335512243442530014366 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local int = math.floor local Write = wg.write local GotoXY = wg.gotoxy local ClearArea = wg.cleararea local SetNormal = wg.setnormal local SetBold = wg.setbold local SetBright = wg.setbright local SetUnderline = wg.setunderline local SetReverse = wg.setreverse local SetDim = wg.setdim local GetStringWidth = wg.getstringwidth local GetBytesOfCharacter = wg.getbytesofcharacter local GetBoundedString = wg.getboundedstring function DrawStatusLine(s) SetReverse() ClearArea(0, ScreenHeight-1, ScreenWidth-1, ScreenHeight-1) Write(0, ScreenHeight-1, s) SetNormal() end function DrawBox(x, y, w, h) local border = string.rep("─", w) local space = string.rep(" ", w) Write(x-1, y, " ┌") Write(x+w+1, y, "┐ ") Write(x-1, y+h+1, " └") Write(x+w+1, y+h+1, "┘ ") Write(x+1, y, border) Write(x+1, y+h+1, border) for i = y+1, y+h do Write(x-1, i, " │") Write(x+w+1, i, "│ ") Write(x+1, i, space) end end function CentreInField(x, y, w, s) s = GetBoundedString(s, w) local xo = int((w - GetStringWidth(s)) / 2) Write(x+xo, y, s) end function LAlignInField(x, y, w, s) s = GetBoundedString(s, w) Write(x, y, s) end function RAlignInField(x, y, w, s) s = GetBoundedString(s, w) local xo = w - GetStringWidth(s) Write(x+xo, y, s) end function DrawTitledBox(x, y, w, h, title, subtitle) SetBright() DrawBox(x, y, w, h) CentreInField(x+1, y, w, title) if subtitle then SetBold() CentreInField(x+1, y+h+1, w, subtitle) end SetNormal() end function ImmediateMessage(text) local w = GetStringWidth(text) local x = int((ScreenWidth - w) / 2) local y = int(ScreenHeight / 2) DrawBox(x-2, y-1, w+2, 1) Write(x, y, text) wg.sync() end function ModalMessage(title, message) local dialogue = { title = title or "Message", width = Form.Large, height = 2, stretchy = true, ["KEY_^C"] = "cancel", [" "] = "confirm", Form.WrappedLabel { value = message, x1 = 1, y1 = 1, x2 = -1, y2 = -3, }, } Form.Run(dialogue, RedrawScreen, "press SPACE to continue") QueueRedraw() end function PromptForYesNo(title, message) local result = nil local function rtrue() result = true return "confirm" end local function rfalse() result = false return "confirm" end local dialogue = { title = title or "Message", width = Form.Large, height = 2, stretchy = true, ["KEY_^C"] = "cancel", ["n"] = rfalse, ["N"] = rfalse, ["y"] = rtrue, ["Y"] = rtrue, Form.WrappedLabel { value = message, x1 = 1, y1 = 1, x2 = -1, y2 = -3, }, } Form.Run(dialogue, RedrawScreen, "Y for yes, N for no, or CTRL+C to cancel") QueueRedraw() return result end function PromptForString(title, message, default) if not default then default = "" end local textfield = Form.TextField { value = default, cursor = default:len() + 1, x1 = 1, y1 = -4, x2 = -1, y2 = -3, } local dialogue = { title = title, width = Form.Large, height = 4, stretchy = true, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", Form.WrappedLabel { value = message, x1 = 1, y1 = 1, x2 = -1, y2 = -6, }, textfield, } local result = Form.Run(dialogue, RedrawScreen, "RETURN to confirm, CTRL+C to cancel") QueueRedraw() if result then return textfield.value else return nil end end function FindAndReplaceDialogue(defaultfind, defaultreplace) defaultfind = defaultfind or "" defaultreplace = defaultreplace or "" local findfield = Form.TextField { value = defaultfind, cursor = defaultfind:len() + 1, x1 = 11, y1 = 1, x2 = -1, y2 = 2, } local replacefield = Form.TextField { value = defaultreplace, cursor = defaultreplace:len() + 1, x1 = 11, y1 = 3, x2 = -1, y2 = 4, } local dialogue = { title = "Find and Replace", width = Form.Large, height = 5, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", Form.Label { value = "Find:", x1 = 1, y1 = 1, x2 = 10, y2 = 1, align = Form.Left, }, Form.Label { value = "Replace:", x1 = 1, y1 = 3, x2 = 10, y2 = 3, align = Form.Left, }, findfield, replacefield, } local result = Form.Run(dialogue, RedrawScreen, "RETURN to confirm, CTRL+C to cancel") QueueRedraw() if result then return findfield.value, replacefield.value else return nil end end function AboutDialogue() local dialogue = { title = "About WordGrinder", width = Form.Large, height = 12, ["KEY_^C"] = "cancel", ["KEY_RETURN"] = "confirm", ["KEY_ENTER"] = "confirm", [" "] = "confirm", Form.Label { value = "WordGrinder "..VERSION, x1 = 1, y1 = 1, x2 = -1, y2 = 1, align = Form.Centre, }, Form.Label { value = "© 2007-2013 David Given", x1 = 1, y1 = 2, x2 = -1, y2 = 2, align = Form.Centre, }, Form.Label { value = "File format version "..FILEFORMAT, x1 = 1, y1 = 4, x2 = -1, y2 = 4, align = Form.Centre, }, Form.Label { value = "Cat vacuuming (n): pointless or otherwise inefficient", x1 = 1, y1 = 6, x2 = -1, y2 = 6, align = Form.Centre, }, Form.Label { value = " displacement activity to avoid having to settle ", x1 = 1, y1 = 7, x2 = -1, y2 = 7, align = Form.Centre, }, Form.Label { value = " down and do some real writing. ", x1 = 1, y1 = 8, x2 = -1, y2 = 8, align = Form.Centre, }, Form.Label { value = "For more information, see http://wordgrinder.sourceforge.net.", x1 = 1, y1 = 10, x2 = -1, y2 = 10, align = Form.Centre, }, } local result = Form.Run(dialogue, RedrawScreen, "press SPACE to continue") QueueRedraw() return nil end wordgrinder-0.5.1.orig/src/lua/utils.lua0000644000000000000000000000426311523520774015116 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. function max(a, b) if (a > b) then return a else return b end end function min(a, b) if (a < b) then return a else return b end end --- Transcodes a string. -- Converts the string to guaranteed valid UTF-8. -- -- @param s string to process -- @return canonicalised string function CanonicaliseString(s) return wg.transcode(s) end --- Chooses between a singular or a plural string. -- -- @param n number -- @param singular returned if number == 1 -- @param plural returned if number ~= 1 -- @return either singular or plural function Pluralise(n, singular, plural) if (n == 1) then return singular else return plural end end --- Extracts the leaf part of a filename by truncating at the last / or \. -- -- @param filename filename -- @return leaf function Leafname(filename) local _, _, f = filename:find("([^/\\]+)$") if f then return f end return filename end --- Produces an exception traceback. -- -- @param e the error -- @return the trace, as a string function Traceback(e) local i = 1 local s = {"Exception: "..e} while true do local t = debug.getinfo(i) if not t then break end s[#s+1] = t.short_src .. ":" .. t.currentline i = i + 1 end return table.concat(s, "\n") end --- Splits a string by specifying the delimiter pattern. -- -- @param str the input string -- @param delim the delimiter -- @return the list of words function SplitString(str, delim) -- Eliminate bad cases... if not str:find(delim) then return {str} end local result = {} local pat = "(.-)" .. delim .. "()" local nb = 0 local lastPos for part, pos in str:gmatch(pat) do nb = nb + 1 result[nb] = part lastPos = pos if (nb == maxNb) then break end end -- Handle the last field if nb ~= maxNb then result[nb + 1] = str:sub(lastPos) end return result end wordgrinder-0.5.1.orig/src/lua/xml.lua0000644000000000000000000001316712123375705014560 0ustar -- © 2013 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local coroutine_yield = coroutine.yield local coroutine_wrap = coroutine.wrap local writeu8 = wg.writeu8 local string_find = string.find local function cowcopy(t) return setmetatable({}, { __index = t } ) end --- Tokenises XML. -- Given an XML string, this function returns an iterator which streams -- tokens from it. -- -- @param xml XML string to tokenise -- @return iterator function TokeniseXML(xml) local PROCESSING = '^<%?([%w_-]+)%s*(.-)%?>' local COMMENT = '^' local CDATA = '^<%!%[CDATA%[(.-)%]%]>' local OPENTAG = '^<%s*([%w_-]+)(:?)([%w+-]*)%s*(.-)(/?)>' local TAGATTR1 = '^%s*([%w_-]+)(:?)([%w+-]*)%s*=%s*"([^"]*)"' local TAGATTR2 = "^%s*([%w_-]+)(:?)([%w+-]*)%s*=%s*'([^']*)'" local CLOSETAG = '^' local TEXT = '^([^&<]+)' local DECIMALENTITY = '^&#(%d+);' local HEXENTITY = '^&#x(%x+);' local NAMEDENTITY = '^&(%w+);' local EOF = '^%s*$' local entities = { ["amp"] = "&", ["lt"] = "<", ["gt"] = ">", ["quot"] = '"', ["apos"] = "'" } -- Collapse whitespace. xml = xml:gsub("\r", "") xml = xml:gsub("\t", " ") xml = xml:gsub(" *\n", "\n") xml = xml:gsub("\n *", "\n") xml = xml:gsub(" +", " ") xml = xml:gsub("\n+", "\n") xml = xml:gsub("([^>])\n([^<])", "%1 %2") xml = xml:gsub("\n", "") xml = xml:gsub("> +<", "><") local offset = 1 local function parse_attributes(scope, data) local attrs = {} local offset = 1 local _, e, s1, s2, s3, s4 while true do while true do _, e, s1, s2, s3, s4 = string_find(data, TAGATTR1, offset) if not e then _, e, s1, s2, s3, s4 = string_find(data, TAGATTR2, offset) end if e then local namespace = "" local name if (s2 ~= "") then namespace = s1 name = s3 else name = s1 end if (namespace == "xmlns") then scope[name] = s4 elseif (namespace == "") and (name == "xmlns") then scope[""] = s4 else attrs[#attrs+1] = { namespace = namespace, name = name, value = s4 } end break end for _, a in ipairs(attrs) do a.namespace = scope[a.namespace] or a.namespace end return attrs end offset = e + 1 end end local parse_tag local function parse_tag_contents(scope) local _, e, s1, s2, s3, s4, s5 while true do while true do _, e = string_find(xml, OPENTAG, offset) if e then parse_tag(scope) break end _, e = string_find(xml, CLOSETAG, offset) if e then offset = e + 1 return end _, e, s1 = string_find(xml, TEXT, offset) if e then coroutine_yield( { event = "text", text = s1 } ) offset = e + 1 break end _, e, s1 = string_find(xml, DECIMALENTITY, offset) if e then coroutine_yield( { event = "text", text = writeu8(tonumber(s1)) } ) offset = e + 1 break end _, e, s1 = string_find(xml, HEXENTITY, offset) if e then coroutine_yield( { event = "text", text = writeu8(tonumber("0x"..s1)) } ) offset = e + 1 break end _, e, s1 = string_find(xml, NAMEDENTITY, offset) if e then coroutine_yield( { event = "text", text = entities[s1] or "invalidentity" } ) offset = e + 1 break end _, e, s1, s2 = string_find(xml, PROCESSING, offset) if s1 then coroutine_yield( { event = "processing", name = s1, attrs = parse_attributes({}, s2) } ) offset = e + 1 break end _, e = string_find(xml, EOF, offset) if e then return end coroutine_yield( { event = "error", text = xml:sub(offset, offset+100) } ) return end end end parse_tag = function(scope) local _, e, s1, s2, s3, s4, s5 = string_find(xml, OPENTAG, offset) local newscope = cowcopy(scope) local tag = { event = "opentag", attrs = parse_attributes(newscope, s4) } if (s2 ~= "") then tag.namespace = newscope[s1] or s1 tag.name = s3 else tag.namespace = newscope[""] or s1 tag.name = s1 end coroutine_yield(tag) offset = e + 1 if (s5 == "") then parse_tag_contents(newscope) end coroutine_yield( { event = "closetag", namespace = tag.namespace, name = tag.name } ) end local function parser() parse_tag_contents({}) end return coroutine_wrap(parser) end --- Parses an XML string into a DOM-ish tree. -- -- @param xml XML string to parse -- @return tree function ParseXML(xml) local nextToken = TokeniseXML(xml) local function parse_tag(token) local n = token.name if (token.namespace ~= "") then n = token.namespace .. " " .. n end local t = { _name = n } for _, a in ipairs(token.attrs) do n = a.name if (a.namespace ~= "") then n = a.namespace .. " " .. n end t[n] = a.value end while true do token = nextToken() if (token.event == "opentag") then t[#t+1] = parse_tag(token) elseif (token.event == "text") then t[#t+1] = token.text elseif (token.event == "closetag") then return t end end end -- Find and parse the first element. while true do local token = nextToken() if (token.event == "opentag") then return parse_tag(token) end if not token then return {} end end end wordgrinder-0.5.1.orig/testdocs/0000755000000000000000000000000012251160512013513 5ustar wordgrinder-0.5.1.orig/testdocs/README-v0.1.wg0000644000000000000000000005246111523516526015514 0ustar WordGrinder dumpfile v1: this is not a text file! DS 0 S styles T 8 T 0 S name S P S below N 1 S html 6 S above 8 S desc S Plain text . T 0 S cstyle N 3 7 8 5 S H1 9 16 10 15 11 S Heading #1 . T 0 14 15 7 8 5 S H2 9 19 10 N 2 11 S Heading #2 . T 0 14 8 7 8 5 S H3 9 23 10 8 11 S Heading #3 . T 0 14 8 7 8 5 S H4 9 26 10 8 11 S Heading #4 . T 0 S indent N 4 7 N 0 5 S Q 9 S BLOCKQUOTE 10 31 11 S Indented text . T 0 29 30 S bullet S - 5 S LB 14 31 10 8 9 S LI 7 8 11 S List item with bullet . T 0 29 30 14 31 7 8 5 S L 9 39 10 8 11 S List item without bullet . 32 28 6 4 23 22 26 25 19 18 42 41 38 35 16 13 . S menu M 0 S accelerators T 0 S S SM S DC S ZDNC S ^K S EN S BACKSPACE S ZDPC S ^I S SB S ^O S SO S PPAGE S ZPGUP S HOME S ZH S ^P S SP S END S ZE S ^R S ER S ^S S FS S RIGHT S ZR S DOWN S ZD S UP S ZU S ^V S EP S NPAGE S ZPGDN S LEFT S ZL S ^U S SU S ^F S EF S ^@ S ZM S ^Q S FQ S ^X S ET S ^C S EC . . S documents T 1 D 33 P 3 W 0 S text S Welcome S x 31 . W 0 101 S to 103 N 8 . W 0 101 S WordGrinder 103 N 11 . S style 13 . P 1 W 0 101 S Introduction 103 31 . 110 18 . P 8 W 0 101 108 103 31 . W 0 101 S is 103 N 12 . W 0 101 S a 103 N 15 . W 0 101 S word 103 N 17 . W 0 101 S processor 103 N 22 . W 0 101 S for 103 N 32 . W 0 101 S processing 103 N 36 . W 0 101 S words. 103 N 47 . 110 4 . P 32 W 0 101 108 103 31 . W 0 101 117 103 118 . W 0 101 S not 103 121 . W 0 101 S WYSIWYG. 103 N 19 . W 0 101 S It 103 N 28 . W 0 101 117 103 N 31 . W 0 101 141 103 N 34 . W 0 101 S point 103 N 38 . W 0 101 S and 103 N 44 . W 0 101 S click. 103 N 48 . W 0 101 146 103 N 55 . W 0 101 117 103 N 58 . W 0 101 141 103 N 61 . W 0 101 120 103 N 65 . W 0 101 S desktop 103 N 67 . W 0 101 S publisher. 103 31 . W 0 101 146 103 109 . W 0 101 117 103 N 14 . W 0 101 141 103 124 . W 0 101 120 103 N 21 . W 0 101 101 103 N 23 . W 0 101 S editor. 103 147 . W 0 101 146 103 133 . W 0 101 117 103 N 39 . W 0 101 141 103 N 42 . W 0 101 S do 103 N 46 . W 0 101 S fonts 103 N 49 . W 0 101 156 103 162 . W 0 101 S it 103 N 59 . W 0 101 S barely 103 N 62 . W 0 101 S does 103 N 69 . W 0 101 S styles. 103 31 . 110 4 . P 21 W 0 101 S What 103 31 . W 0 101 197 103 N 5 . W 0 101 203 103 106 . W 0 101 190 103 N 13 . W 0 101 117 103 N 16 . W 0 101 135 103 144 . W 0 101 S It's 103 N 26 . W 0 101 S designed 103 149 . W 0 101 129 103 N 40 . W 0 101 S writing 103 157 . W 0 101 S text. 103 N 52 . W 0 101 146 103 164 . W 0 101 S gets 103 166 . W 0 101 S out 103 N 66 . W 0 101 S of 103 N 70 . W 0 101 S your 103 N 73 . W 0 101 S way 103 31 . W 0 101 156 103 30 . W 0 101 S lets 103 106 . W 0 101 S you 103 214 . W 0 101 S type. 103 124 . 110 4 . P 2 W 0 101 S Survival 103 31 . W 0 101 S Guide 103 N 9 . 110 18 . P 16 W 0 101 S All 103 31 . W 0 101 240 103 30 . W 0 101 S favourite 103 256 . W 0 101 S navigation 103 144 . W 0 101 S keys 103 N 30 . W 0 101 S should 103 N 35 . W 0 101 S work. 103 188 . W 0 101 S Just 103 160 . W 0 101 S type 103 N 53 . W 0 101 156 103 164 . W 0 101 197 103 201 . W 0 101 269 103 168 . W 0 101 190 103 N 72 . W 0 101 S the 103 N 75 . W 0 101 S right 103 31 . W 0 101 S thing. 103 N 6 . 110 4 . P 63 W 0 101 S For 103 31 . W 0 101 S doing 103 30 . W 0 101 S anything 103 N 10 . W 0 101 S complicated, 103 144 . W 0 101 S you'll 103 130 . W 0 101 S want 103 186 . W 0 101 105 103 157 . W 0 101 S use 103 136 . W 0 101 284 103 N 51 . W 0 101 S menu. 103 162 . W 0 101 S Press 103 166 . W 0 101 S ESC 103 171 . W 0 101 105 103 N 71 . W 0 101 S open 103 N 74 . W 0 101 S it. 103 31 . W 0 101 S Once 103 30 . W 0 101 S open, 103 256 . W 0 101 248 103 121 . W 0 101 S can 103 144 . W 0 101 S select 103 181 . W 0 101 S an 103 267 . W 0 101 S item 103 N 33 . W 0 101 S by 103 154 . W 0 101 S using 103 N 41 . W 0 101 284 103 136 . W 0 101 S cursor 103 309 . W 0 101 266 103 164 . W 0 101 156 103 N 63 . W 0 101 S RETURN 103 171 . W 0 101 S or 103 320 . W 0 101 S RIGHT. 103 31 . W 0 101 S You 103 N 7 . W 0 101 329 103 109 . W 0 101 S also 103 121 . W 0 101 S press 103 N 20 . W 0 101 284 103 220 . W 0 101 S highlighted 103 267 . W 0 101 S key 103 188 . W 0 101 105 103 191 . W 0 101 284 103 194 . W 0 101 S left 103 277 . W 0 101 237 103 164 . W 0 101 284 103 166 . W 0 101 44 103 168 . W 0 101 335 103 238 . W 0 101 129 103 285 . W 0 101 S fast 103 31 . W 0 101 S navigation. 103 211 . W 0 101 313 103 124 . W 0 101 S LEFT 103 181 . W 0 101 105 103 147 . W 0 101 S back 103 149 . W 0 101 234 103 133 . W 0 101 237 103 224 . W 0 101 120 103 N 43 . W 0 101 S submenu. 103 N 45 . W 0 101 S Backing 103 N 54 . W 0 101 234 103 201 . W 0 101 237 103 235 . W 0 101 284 103 204 . W 0 101 S top-level 103 31 . W 0 101 44 103 298 . W 0 101 S cancels. 103 121 . 110 4 . P 83 W 0 101 S Most 103 31 . W 0 101 S platforms 103 211 . W 0 101 S enforce 103 121 . W 0 101 120 103 181 . W 0 101 S one 103 N 25 . W 0 101 S second 103 N 29 . W 0 101 S delay 103 133 . W 0 101 S after 103 188 . W 0 101 S pressing 103 160 . W 0 101 315 103 N 57 . W 0 101 S before 103 166 . W 0 101 284 103 N 68 . W 0 101 44 103 282 . W 0 101 S actually 103 31 . W 0 101 S appears. 103 256 . W 0 101 S Sorry, 103 N 18 . W 0 101 S I 103 415 . W 0 101 S can't 103 N 27 . W 0 101 190 103 336 . W 0 101 297 103 133 . W 0 101 S about 103 393 . W 0 101 S this... 103 309 . W 0 101 S but 103 198 . W 0 101 248 103 347 . W 0 101 S don't 103 171 . W 0 101 S have 103 241 . W 0 101 105 103 31 . W 0 101 S wait. 103 15 . W 0 101 S If 103 256 . W 0 101 248 103 118 . W 0 101 304 103 216 . W 0 101 284 103 179 . W 0 101 S file 103 415 . W 0 101 S menu, 103 267 . W 0 101 424 103 133 . W 0 101 S ESC, 103 393 . W 0 101 S F 103 N 50 . W 0 101 S will 103 229 . W 0 101 S cause 103 426 . W 0 101 284 103 347 . W 0 101 S File 103 171 . W 0 101 44 103 282 . W 0 101 105 103 31 . W 0 101 S appear 103 15 . W 0 101 284 103 298 . W 0 101 S instant 103 176 . W 0 101 248 103 127 . W 0 101 361 103 220 . W 0 101 S F. 103 130 . W 0 101 S On 103 270 . W 0 101 S some 103 154 . W 0 101 S platforms, 103 390 . W 0 101 248 103 396 . W 0 101 S may 103 164 . W 0 101 S be 103 201 . W 0 101 S able 103 168 . W 0 101 105 103 238 . W 0 101 S speed 103 241 . W 0 101 S things 103 31 . W 0 101 S up 103 356 . W 0 101 S even 103 298 . W 0 101 S further 103 121 . W 0 101 338 103 181 . W 0 101 424 103 220 . W 0 101 S ALT+F. 103 270 . W 0 101 461 103 188 . W 0 101 S you're 103 393 . W 0 101 S on 103 229 . W 0 101 414 103 162 . W 0 101 237 103 198 . W 0 101 S these, 103 201 . W 0 101 248 103 204 . W 0 101 501 103 241 . W 0 101 S find 103 31 . W 0 101 197 103 211 . W 0 101 S convenient 103 106 . W 0 101 105 103 144 . W 0 101 307 103 127 . W 0 101 S ALT+. 103 220 . W 0 101 105 103 130 . W 0 101 319 103 270 . W 0 101 284 103 224 . W 0 101 311 103 157 . 110 4 . P 36 W 0 101 461 103 31 . W 0 101 248 103 15 . W 0 101 S see 103 356 . W 0 101 120 103 109 . W 0 101 367 103 214 . W 0 101 S combination 103 124 . W 0 101 105 103 418 . W 0 101 284 103 130 . W 0 101 287 103 133 . W 0 101 237 103 188 . W 0 101 120 103 393 . W 0 101 44 103 136 . W 0 101 S item, 103 229 . W 0 101 S that 103 164 . W 0 101 S means 103 347 . W 0 101 S it's 103 204 . W 0 101 120 103 320 . W 0 101 S shortcut. 103 31 . W 0 101 313 103 298 . W 0 101 284 103 216 . W 0 101 367 103 362 . W 0 101 553 103 N 24 . W 0 101 S without 103 133 . W 0 101 S opening 103 157 . W 0 101 284 103 229 . W 0 101 44 103 N 56 . W 0 101 156 103 166 . W 0 101 563 103 168 . W 0 101 335 103 238 . W 0 101 476 103 31 . W 0 101 503 103 211 . W 0 101 S invoked. 103 106 . W 0 101 S They're 103 124 . W 0 101 S configurable. 103 415 . W 0 101 S See 103 186 . W 0 101 S below. 103 390 . 110 4 . P 41 W 0 101 355 103 31 . W 0 101 329 103 30 . W 0 101 S load 103 106 . W 0 101 156 103 214 . W 0 101 S save 103 124 . W 0 101 S files 103 127 . W 0 101 340 103 147 . W 0 101 284 103 151 . W 0 101 481 103 154 . W 0 101 311 103 390 . W 0 101 S A 103 194 . W 0 101 S Document 103 309 . W 0 101 S Set 103 N 60 . W 0 101 117 103 N 64 . W 0 101 120 103 171 . W 0 101 466 103 204 . W 0 101 525 103 320 . W 0 101 S disk. 103 31 . W 0 101 S Each 103 290 . W 0 101 466 103 109 . W 0 101 329 103 216 . W 0 101 S contain 103 362 . W 0 101 S multiple 103 147 . W 0 101 S Documents. 103 N 37 . W 0 101 S Pay 103 160 . W 0 101 S careful 103 229 . W 0 101 S attention 103 618 . W 0 101 105 103 238 . W 0 101 S whether 103 31 . W 0 101 108 103 106 . W 0 101 117 103 362 . W 0 101 S talking 103 181 . W 0 101 447 103 149 . W 0 101 S Documents 103 636 . W 0 101 351 103 136 . W 0 101 S Document 103 474 . W 0 101 S Sets. 103 198 . W 0 101 S There's 103 168 . W 0 101 S more 103 241 . W 0 101 S information 103 31 . W 0 101 597 103 118 . 110 4 . P 68 W 0 101 S Some 103 31 . W 0 101 S operations 103 211 . W 0 101 S (applying 103 216 . W 0 101 S character 103 220 . W 0 101 S styles, 103 133 . W 0 101 S cut 103 157 . W 0 101 156 103 160 . W 0 101 S paste) 103 229 . W 0 101 S require 103 198 . W 0 101 248 103 171 . W 0 101 105 103 317 . W 0 101 331 103 31 . W 0 101 228 103 356 . W 0 101 S To 103 214 . W 0 101 190 103 216 . W 0 101 S this, 103 144 . W 0 101 S move 103 415 . W 0 101 105 103 267 . W 0 101 414 103 336 . W 0 101 S end 103 636 . W 0 101 237 103 341 . W 0 101 284 103 157 . W 0 101 S area 103 160 . W 0 101 105 103 277 . W 0 101 S mark; 103 582 . W 0 101 361 103 201 . W 0 101 S CTRL+SPACE; 103 31 . W 0 101 693 103 118 . W 0 101 105 103 124 . W 0 101 284 103 362 . W 0 101 S other 103 575 . W 0 101 S end. 103 267 . W 0 101 S The 103 270 . W 0 101 S operation 103 186 . W 0 101 476 103 194 . W 0 101 503 103 396 . W 0 101 S applied 103 426 . W 0 101 105 103 168 . W 0 101 284 103 430 . W 0 101 365 103 31 . W 0 101 101 103 118 . W 0 101 S between 103 124 . W 0 101 284 103 415 . W 0 101 S mark 103 418 . W 0 101 156 103 151 . W 0 101 284 103 154 . W 0 101 S cursor. 103 188 . W 0 101 S Note 103 474 . W 0 101 563 103 162 . W 0 101 284 103 618 . W 0 101 S marks 103 620 . W 0 101 S are 103 238 . W 0 101 S always 103 31 . W 0 101 S placed 103 356 . W 0 101 525 103 176 . W 0 101 284 103 124 . W 0 101 S left 103 179 . W 0 101 S side 103 220 . W 0 101 237 103 149 . W 0 101 284 103 151 . W 0 101 735 103 154 . W 0 101 S Pressing 103 191 . W 0 101 S CTRL+SPACE 103 162 . W 0 101 S again 103 235 . W 0 101 476 103 282 . W 0 101 S remove 103 31 . W 0 101 284 103 356 . W 0 101 S marks. 103 109 . 110 4 . P 7 W 0 101 S That's 103 31 . W 0 101 322 103 356 . W 0 101 S Now 103 109 . W 0 101 248 103 121 . W 0 101 329 103 144 . W 0 101 S work 103 181 . W 0 101 S WordGrinder. 103 147 . 110 4 . P 1 W 0 101 S Styles 103 31 . 110 18 . P 6 W 0 101 108 103 31 . W 0 101 S supports 103 118 . W 0 101 284 103 179 . W 0 101 S following 103 415 . W 0 101 673 103 270 . W 0 101 S styles: 103 393 . 110 4 . P 1 W 0 101 S Italic 103 31 . 110 35 . P 1 W 0 101 S Underline 103 31 . 110 35 . P 31 W 0 101 771 103 31 . W 0 101 322 103 356 . W 0 101 355 103 109 . W 0 101 329 103 121 . W 0 101 307 103 144 . W 0 101 S combinations 103 181 . W 0 101 S if 103 133 . W 0 101 248 103 186 . W 0 101 S like. 103 390 . W 0 101 688 103 194 . W 0 101 S apply 103 229 . W 0 101 S them, 103 164 . W 0 101 331 103 620 . W 0 101 496 103 317 . W 0 101 101 103 31 . W 0 101 156 103 211 . W 0 101 S then 103 256 . W 0 101 307 103 176 . W 0 101 S Style▷Italic 103 438 . W 0 101 S (CTRL+I) 103 149 . W 0 101 351 103 224 . W 0 101 S Style▷Underline 103 390 . W 0 101 S (CTRL+B) 103 198 . W 0 101 105 103 430 . W 0 101 S apply. 103 317 . W 0 101 688 103 31 . W 0 101 765 103 15 . W 0 101 675 103 298 . W 0 101 307 103 438 . W 0 101 S Style▷Plain 103 127 . W 0 101 S (CTRL+O). 103 151 . 110 4 . P 50 W 0 101 S There 103 31 . W 0 101 S are, 103 290 . W 0 101 S however, 103 109 . W 0 101 120 103 362 . W 0 101 S number 103 127 . W 0 101 237 103 418 . W 0 101 S paragraph 103 130 . W 0 101 2 103 188 . W 0 101 S supported. 103 194 . W 0 101 595 103 618 . W 0 101 S Style▷Paragraph 103 31 . W 0 101 783 103 216 . W 0 101 S (CTRL+P) 103 181 . W 0 101 129 103 130 . W 0 101 284 103 133 . W 0 101 S list. 103 224 . W 0 101 627 103 191 . W 0 101 110 103 309 . W 0 101 S applies 103 426 . W 0 101 105 103 168 . W 0 101 120 103 430 . W 0 101 S complete 103 238 . W 0 101 S paragraph. 103 31 . W 0 101 461 103 109 . W 0 101 248 103 176 . W 0 101 304 103 438 . W 0 101 105 103 181 . W 0 101 549 103 220 . W 0 101 S what 103 267 . W 0 101 110 103 270 . W 0 101 S each 103 341 . W 0 101 857 103 191 . W 0 101 S has 103 582 . W 0 101 S set, 103 618 . W 0 101 307 103 168 . W 0 101 S Style▷Set 103 204 . W 0 101 S Margin 103 31 . W 0 101 S Mode▷Show 103 356 . W 0 101 S Paragraph 103 124 . W 0 101 S Styles. 103 443 . W 0 101 716 103 270 . W 0 101 110 103 186 . W 0 101 S names 103 393 . W 0 101 476 103 309 . W 0 101 503 103 582 . W 0 101 S displayed 103 198 . W 0 101 S in 103 204 . W 0 101 284 103 282 . W 0 101 S left-hand 103 31 . W 0 101 S margin. 103 298 . 110 4 . P 17 W 0 101 904 103 31 . W 0 101 2 103 298 . W 0 101 743 103 124 . W 0 101 141 103 179 . W 0 101 S currently 103 415 . W 0 101 S customisable, 103 270 . W 0 101 451 103 194 . W 0 101 197 103 277 . W 0 101 117 103 582 . W 0 101 S intended 103 198 . W 0 101 563 103 430 . W 0 101 S they 103 241 . W 0 101 476 103 31 . W 0 101 503 103 211 . W 0 101 916 103 106 . W 0 101 284 103 109 . W 0 101 S future. 103 121 . 110 4 . P 4 W 0 101 652 103 31 . W 0 101 156 103 298 . W 0 101 655 103 176 . W 0 101 S Sets 103 181 . 110 18 . P 29 W 0 101 627 103 31 . W 0 101 108 103 211 . W 0 101 466 103 124 . W 0 101 329 103 127 . W 0 101 631 103 220 . W 0 101 633 103 151 . W 0 101 S pieces 103 390 . W 0 101 237 103 474 . W 0 101 228 103 277 . W 0 101 613 103 198 . W 0 101 466 103 166 . W 0 101 117 103 235 . W 0 101 S referred 103 204 . W 0 101 105 103 31 . W 0 101 S as 103 15 . W 0 101 120 103 290 . W 0 101 615 103 106 . W 0 101 S Set; 103 124 . W 0 101 890 103 127 . W 0 101 S piece 103 443 . W 0 101 237 103 336 . W 0 101 101 103 133 . W 0 101 S within 103 341 . W 0 101 284 103 160 . W 0 101 655 103 229 . W 0 101 S Set 103 166 . W 0 101 117 103 168 . W 0 101 120 103 430 . W 0 101 S Document. 103 31 . 110 4 . P 39 W 0 101 355 103 31 . W 0 101 329 103 30 . W 0 101 456 103 106 . W 0 101 969 103 214 . W 0 101 S many 103 216 . W 0 101 96 103 179 . W 0 101 916 103 149 . W 0 101 120 103 151 . W 0 101 S document 103 133 . W 0 101 S set 103 393 . W 0 101 969 103 194 . W 0 101 248 103 229 . W 0 101 S like 103 582 . W 0 101 S (within 103 166 . W 0 101 S reason...). 103 31 . W 0 101 355 103 118 . W 0 101 329 103 216 . W 0 101 S manage 103 362 . W 0 101 S them 103 443 . W 0 101 340 103 130 . W 0 101 284 103 154 . W 0 101 S entries 103 188 . W 0 101 916 103 474 . W 0 101 284 103 277 . W 0 101 481 103 426 . W 0 101 311 103 201 . W 0 101 355 103 430 . W 0 101 329 103 282 . W 0 101 549 103 31 . W 0 101 156 103 30 . W 0 101 S switch 103 106 . W 0 101 728 103 121 . W 0 101 96 103 181 . W 0 101 340 103 336 . W 0 101 284 103 186 . W 0 101 652 103 390 . W 0 101 44 103 277 . W 0 101 S (ESC, 103 164 . W 0 101 S D). 103 620 . 110 4 . P 37 W 0 101 652 103 31 . W 0 101 456 103 298 . W 0 101 S their 103 121 . W 0 101 S own 103 179 . W 0 101 S text, 103 415 . W 0 101 S selection, 103 149 . W 0 101 S viewing 103 188 . W 0 101 S style, 103 474 . W 0 101 344 103 426 . W 0 101 S position, 103 620 . W 0 101 S etc. 103 320 . W 0 101 716 103 31 . W 0 101 S clipboard, 103 30 . W 0 101 851 103 121 . W 0 101 117 103 575 . W 0 101 S shared 103 443 . W 0 101 S across 103 151 . W 0 101 284 103 341 . W 0 101 S entire 103 393 . W 0 101 1000 103 229 . W 0 101 S set. 103 166 . W 0 101 S This 103 235 . W 0 101 S allows 103 317 . W 0 101 248 103 31 . W 0 101 105 103 30 . W 0 101 693 103 356 . W 0 101 959 103 118 . W 0 101 237 103 144 . W 0 101 101 103 127 . W 0 101 S around 103 443 . W 0 101 980 103 151 . W 0 101 284 103 341 . W 0 101 1000 103 393 . W 0 101 1002 103 396 . W 0 101 340 103 164 . W 0 101 284 103 620 . W 0 101 S clipboard. 103 430 . 110 4 . P 16 W 0 101 S Importing 103 31 . W 0 101 S non-WordGrinder 103 298 . W 0 101 607 103 220 . W 0 101 745 103 130 . W 0 101 S results 103 186 . W 0 101 916 103 136 . W 0 101 120 103 474 . W 0 101 S new 103 229 . W 0 101 1000 103 582 . W 0 101 S being 103 168 . W 0 101 S added 103 317 . W 0 101 105 103 31 . W 0 101 284 103 15 . W 0 101 S current 103 356 . W 0 101 1000 103 121 . W 0 101 1075 103 575 . 110 4 . P 2 W 0 103 31 101 S Menu . W 0 103 211 101 S customisation . 110 18 . P 32 W 0 103 31 101 355 . W 0 103 30 101 329 . W 0 103 106 101 S change . W 0 103 121 101 S any . W 0 103 144 101 237 . W 0 103 127 101 284 . W 0 103 220 101 S keyboard . W 0 103 270 101 S shortcuts . W 0 103 393 101 S assigned . W 0 103 396 101 105 . W 0 103 426 101 44 . W 0 103 201 101 S items. . W 0 103 204 101 S These . W 0 103 285 101 743 . W 0 103 31 101 823 . W 0 103 211 101 S saved . W 0 103 109 101 969 . W 0 103 176 101 S part . W 0 103 144 101 237 . W 0 103 127 101 284 . W 0 103 220 101 1000 . W 0 103 270 101 895 . W 0 103 224 101 S so . W 0 103 390 101 890 . W 0 103 160 101 108 . W 0 103 618 101 466 . W 0 103 168 101 S takes . W 0 103 317 101 S its . W 0 103 31 101 1135 . W 0 103 256 101 S configuration . W 0 103 181 101 S with . W 0 103 147 101 322 . 110 4 . P 79 W 0 103 31 101 688 . W 0 103 15 101 190 . W 0 103 290 101 691 . W 0 103 118 101 319 . W 0 103 124 101 284 . W 0 103 179 101 468 . W 0 103 443 101 S navigate . W 0 103 133 101 105 . W 0 103 186 101 284 . W 0 103 390 101 S entry . W 0 103 194 101 248 . W 0 103 277 101 304 . W 0 103 164 101 105 . W 0 103 166 101 S change, . W 0 103 204 101 156 . W 0 103 241 101 823 . W 0 103 31 101 361 . W 0 103 290 101 S CTRL+V . W 0 103 214 101 S followed . W 0 103 127 101 338 . W 0 103 415 101 284 . W 0 103 418 101 367 . W 0 103 336 101 248 . W 0 103 636 101 304 . W 0 103 188 101 105 . W 0 103 393 101 S bind. . W 0 103 309 101 355 . W 0 103 162 101 442 . W 0 103 166 101 S bind . W 0 103 235 101 284 . W 0 103 238 101 101 . W 0 103 31 101 1184 . W 0 103 290 101 S keys. . W 0 103 118 101 355 . W 0 103 216 101 359 . W 0 103 179 101 442 . W 0 103 443 101 1208 . W 0 103 130 101 120 . W 0 103 151 101 367 . W 0 103 154 101 S that's . W 0 103 393 101 S already . W 0 103 277 101 916 . W 0 103 582 101 307 . W 0 103 618 101 S elsewhere. . W 0 103 317 101 355 . W 0 103 285 101 329 . W 0 103 31 101 765 . W 0 103 356 101 120 . W 0 103 256 101 S binding . W 0 103 124 101 338 . W 0 103 362 101 S navigating . W 0 103 149 101 105 . W 0 103 151 101 284 . W 0 103 154 101 S appropriate . W 0 103 474 101 44 . W 0 103 162 101 335 . W 0 103 618 101 156 . W 0 103 620 101 424 . W 0 103 31 101 S CTRL+X. . W 0 103 106 101 355 . W 0 103 118 101 456 . W 0 103 124 101 105 . W 0 103 362 101 S manually . W 0 103 418 101 765 . W 0 103 133 101 120 . W 0 103 154 101 1233 . W 0 103 191 101 S from . W 0 103 309 101 120 . W 0 103 277 101 44 . W 0 103 164 101 335 . W 0 103 347 101 428 . W 0 103 238 101 248 . W 0 103 320 101 329 . W 0 103 31 101 1208 . W 0 103 211 101 120 . W 0 103 356 101 S different . W 0 103 124 101 367 . W 0 103 179 101 105 . W 0 103 575 101 322 . 110 4 . P 28 W 0 103 31 101 259 . W 0 103 30 101 108 . W 0 103 216 101 S keybindings . W 0 103 147 101 S (except . W 0 103 133 101 471 . W 0 103 341 101 156 . W 0 103 393 101 44 . W 0 103 474 101 156 . W 0 103 396 101 S dialogue . W 0 103 347 101 S navigation) . W 0 103 31 101 485 . W 0 103 356 101 525 . W 0 103 298 101 284 . W 0 103 176 101 S menus, . W 0 103 179 101 1158 . W 0 103 575 101 S they're . W 0 103 130 101 S all . W 0 103 133 101 593 . W 0 103 474 101 S Things . W 0 103 426 101 1006 . W 0 103 201 101 284 . W 0 103 235 101 344 . W 0 103 241 101 266 . W 0 103 31 101 485 . W 0 103 356 101 916 . W 0 103 298 101 284 . W 0 103 176 101 S Navigation . W 0 103 415 101 311 . 110 4 . P 1 W 0 103 31 101 S Unicode . 110 18 . P 3 W 0 103 31 101 108 . W 0 103 118 101 787 . W 0 103 179 101 S UNICODE! . 110 4 . P 50 W 0 103 31 101 S Mostly, . W 0 103 106 101 S at . W 0 103 109 101 1131 . W 0 103 121 101 S rate. . W 0 103 179 101 S Combining . W 0 103 149 101 S characters . W 0 103 188 101 156 . W 0 103 191 101 S right-to-left . W 0 103 618 101 743 . W 0 103 620 101 S unsupported. . W 0 103 31 101 1077 . W 0 103 211 101 565 . W 0 103 109 101 563 . W 0 103 216 101 248 . W 0 103 362 101 329 . W 0 103 575 101 S use, . W 0 103 418 101 S say, . W 0 103 151 101 S Français, . W 0 103 157 101 S русский . W 0 103 229 101 S язык, . W 0 103 164 101 S 한국어 . W 0 103 168 101 156 . W 0 103 204 101 S 日本語, . W 0 103 31 101 451 . W 0 103 30 101 248 . W 0 103 106 101 442 . W 0 103 176 101 307 . W 0 103 438 101 S Arabic . W 0 103 415 101 351 . W 0 103 147 101 S Hebrew . W 0 103 270 101 S because . W 0 103 390 101 938 . W 0 103 160 101 697 . W 0 103 229 101 512 . W 0 103 162 101 S mangled . W 0 103 347 101 S (العربية . W 0 103 282 101 156 . W 0 103 31 101 S עברית . W 0 103 290 101 S respectively). . W 0 103 179 101 355 . W 0 103 415 101 329 . W 0 103 418 101 307 . W 0 103 336 101 S line . W 0 103 154 101 S art . W 0 103 188 101 451 . W 0 103 191 101 1221 . W 0 103 277 101 141 . W 0 103 426 101 887 . W 0 103 201 101 S WordGrinder's . W 0 103 31 101 S for. . 110 4 . P 23 W 0 103 31 101 S Unfortunately, . W 0 103 121 101 S how . W 0 103 144 101 105 . W 0 103 127 101 433 . W 0 103 149 101 S enter . W 0 103 636 101 1310 . W 0 103 393 101 1327 . W 0 103 582 101 117 . W 0 103 198 101 S entirely . W 0 103 430 101 512 . W 0 103 317 101 105 . W 0 103 320 101 240 . W 0 103 31 101 S operating . W 0 103 298 101 S system . W 0 103 124 101 156 . W 0 103 179 101 240 . W 0 103 220 101 S terminal . W 0 103 270 101 S application. . W 0 103 160 101 108 . W 0 103 618 101 442 . W 0 103 235 101 S help . W 0 103 317 101 248 . W 0 103 31 101 S there. . 110 4 . S viewmode 8 S wrapwidth N 80 S co 211 S cw 8 5 S README S cp 144 S margin 31 . 1431 98 . 1117 98 5 S README.wg . wordgrinder-0.5.1.orig/testdocs/README-v0.2.wg0000644000000000000000000007601611523516526015517 0ustar WordGrinder dumpfile v1: this is not a text file! DS 0 S styles T 8 T 0 S name S P S below N 1 S html 6 S above 8 S desc S Plain text . T 0 S cstyle N 3 7 8 5 S H1 9 16 10 15 11 S Heading #1 . T 0 14 15 7 8 5 S H2 9 19 10 N 2 11 S Heading #2 . T 0 14 8 7 8 5 S H3 9 23 10 8 11 S Heading #3 . T 0 14 8 7 8 5 S H4 9 26 10 8 11 S Heading #4 . T 0 S indent N 4 7 N 0 5 S Q 9 S BLOCKQUOTE 10 31 11 S Indented text . T 0 29 30 S bullet S - 5 S LB 14 31 10 8 9 S LI 7 8 11 S List item with bullet . T 0 29 30 14 31 7 8 5 S L 9 39 10 8 11 S List item without bullet . 32 28 6 4 23 22 26 25 19 18 42 41 38 35 16 13 . S fileformat 20 S documents T 1 D 53 P 3 W 0 S text S Welcome S x 31 . W 0 50 S to 52 N 8 . W 0 50 S WordGrinder 52 N 11 . S style 13 . P 1 W 0 50 S Introduction 52 31 . 59 18 . P 8 W 0 50 57 52 31 . W 0 50 S is 52 N 12 . W 0 50 S a 52 N 15 . W 0 50 S word 52 N 17 . W 0 50 S processor 52 N 22 . W 0 50 S for 52 N 32 . W 0 50 S processing 52 N 36 . W 0 50 S words. 52 N 47 . 59 4 . P 32 W 0 50 57 52 31 . W 0 50 66 52 67 . W 0 50 S not 52 70 . W 0 50 S WYSIWYG. 52 N 19 . W 0 50 S It 52 N 28 . W 0 50 66 52 N 31 . W 0 50 90 52 N 34 . W 0 50 S point 52 N 38 . W 0 50 S and 52 N 44 . W 0 50 S click. 52 N 48 . W 0 50 95 52 N 55 . W 0 50 66 52 N 58 . W 0 50 90 52 N 61 . W 0 50 69 52 N 65 . W 0 50 S desktop 52 N 67 . W 0 50 S publisher. 52 31 . W 0 50 95 52 58 . W 0 50 66 52 N 14 . W 0 50 90 52 73 . W 0 50 69 52 N 21 . W 0 50 50 52 N 23 . W 0 50 S editor. 52 96 . W 0 50 95 52 82 . W 0 50 66 52 N 39 . W 0 50 90 52 N 42 . W 0 50 S do 52 N 46 . W 0 50 S fonts 52 N 49 . W 0 50 105 52 111 . W 0 50 S it 52 N 59 . W 0 50 S barely 52 N 62 . W 0 50 S does 52 N 69 . W 0 50 S styles. 52 31 . 59 4 . P 21 W 0 50 S What 52 31 . W 0 50 146 52 N 5 . W 0 50 152 52 55 . W 0 50 139 52 N 13 . W 0 50 66 52 N 16 . W 0 50 84 52 93 . W 0 50 S It's 52 N 26 . W 0 50 S designed 52 98 . W 0 50 78 52 N 40 . W 0 50 S writing 52 106 . W 0 50 S text. 52 N 52 . W 0 50 95 52 113 . W 0 50 S gets 52 115 . W 0 50 S out 52 N 66 . W 0 50 S of 52 N 70 . W 0 50 S your 52 N 73 . W 0 50 S way 52 31 . W 0 50 105 52 30 . W 0 50 S lets 52 55 . W 0 50 S you 52 163 . W 0 50 S type. 52 73 . 59 4 . P 2 W 0 50 S Survival 52 31 . W 0 50 S Guide 52 N 9 . 59 18 . P 16 W 0 50 S All 52 31 . W 0 50 189 52 30 . W 0 50 S favourite 52 205 . W 0 50 S navigation 52 93 . W 0 50 S keys 52 N 30 . W 0 50 S should 52 N 35 . W 0 50 S work. 52 137 . W 0 50 S Just 52 109 . W 0 50 S type 52 N 53 . W 0 50 105 52 113 . W 0 50 146 52 150 . W 0 50 218 52 117 . W 0 50 139 52 N 72 . W 0 50 S the 52 31 . W 0 50 S right 52 30 . W 0 50 S thing. 52 N 10 . 59 4 . P 63 W 0 50 S For 52 31 . W 0 50 S doing 52 30 . W 0 50 S anything 52 238 . W 0 50 S complicated, 52 93 . W 0 50 S you'll 52 79 . W 0 50 S want 52 135 . W 0 50 54 52 106 . W 0 50 S use 52 85 . W 0 50 233 52 N 51 . W 0 50 S menu. 52 111 . W 0 50 S Press 52 115 . W 0 50 S ESC 52 120 . W 0 50 54 52 N 71 . W 0 50 S open 52 31 . W 0 50 S it. 52 160 . W 0 50 S Once 52 205 . W 0 50 S open, 52 125 . W 0 50 197 52 N 20 . W 0 50 S can 52 N 24 . W 0 50 S select 52 96 . W 0 50 S an 52 219 . W 0 50 S item 52 103 . W 0 50 S by 52 N 43 . W 0 50 S using 52 140 . W 0 50 233 52 178 . W 0 50 S cursor 52 N 56 . W 0 50 215 52 N 63 . W 0 50 105 52 N 68 . W 0 50 S RETURN 52 31 . W 0 50 S or 52 N 7 . W 0 50 S RIGHT. 52 238 . W 0 50 S You 52 73 . W 0 50 276 52 128 . W 0 50 S also 52 N 25 . W 0 50 S press 52 216 . W 0 50 233 52 82 . W 0 50 S highlighted 52 173 . W 0 50 S key 52 178 . W 0 50 54 52 292 . W 0 50 233 52 147 . W 0 50 S left 52 294 . W 0 50 186 52 296 . W 0 50 233 52 264 . W 0 50 S menu 52 31 . W 0 50 283 52 160 . W 0 50 78 52 238 . W 0 50 S fast 52 125 . W 0 50 S navigation. 52 93 . W 0 50 260 52 98 . W 0 50 S LEFT 52 N 37 . W 0 50 54 52 137 . W 0 50 S back 52 N 45 . W 0 50 183 52 N 50 . W 0 50 186 52 N 54 . W 0 50 69 52 N 57 . W 0 50 S submenu. 52 147 . W 0 50 S Backing 52 296 . W 0 50 183 52 31 . W 0 50 186 52 30 . W 0 50 233 52 301 . W 0 50 S top-level 52 58 . W 0 50 324 52 128 . W 0 50 S cancels. 52 169 . 59 4 . P 83 W 0 50 S Most 52 31 . W 0 50 S platforms 52 160 . W 0 50 S enforce 52 70 . W 0 50 69 52 130 . W 0 50 S one 52 309 . W 0 50 S second 52 N 29 . W 0 50 S delay 52 82 . W 0 50 S after 52 137 . W 0 50 S pressing 52 109 . W 0 50 262 52 344 . W 0 50 S before 52 115 . W 0 50 233 52 296 . W 0 50 324 52 231 . W 0 50 S actually 52 31 . W 0 50 S appears. 52 205 . W 0 50 S Sorry, 52 N 18 . W 0 50 S I 52 309 . W 0 50 S can't 52 N 27 . W 0 50 139 52 N 33 . W 0 50 245 52 82 . W 0 50 S about 52 338 . W 0 50 S this... 52 256 . W 0 50 S but 52 147 . W 0 50 197 52 294 . W 0 50 S don't 52 120 . W 0 50 S have 52 190 . W 0 50 54 52 31 . W 0 50 S wait. 52 15 . W 0 50 S If 52 205 . W 0 50 197 52 67 . W 0 50 251 52 165 . W 0 50 233 52 128 . W 0 50 S file 52 309 . W 0 50 S menu, 52 216 . W 0 50 375 52 82 . W 0 50 S ESC, 52 338 . W 0 50 S F 52 340 . W 0 50 S will 52 178 . W 0 50 S cause 52 344 . W 0 50 233 52 294 . W 0 50 S File 52 120 . W 0 50 324 52 231 . W 0 50 54 52 31 . W 0 50 S appear 52 15 . W 0 50 233 52 238 . W 0 50 S instant 52 125 . W 0 50 197 52 76 . W 0 50 311 52 169 . W 0 50 S F. 52 79 . W 0 50 S On 52 219 . W 0 50 S some 52 103 . W 0 50 S platforms, 52 286 . W 0 50 197 52 342 . W 0 50 S may 52 113 . W 0 50 S be 52 150 . W 0 50 S able 52 117 . W 0 50 54 52 187 . W 0 50 S speed 52 31 . W 0 50 S things 52 N 6 . W 0 50 S up 52 163 . W 0 50 S even 52 165 . W 0 50 S further 52 128 . W 0 50 285 52 369 . W 0 50 375 52 79 . W 0 50 S ALT+F. 52 N 41 . W 0 50 411 52 109 . W 0 50 S you're 52 256 . W 0 50 S on 52 113 . W 0 50 366 52 115 . W 0 50 186 52 117 . W 0 50 S these, 52 296 . W 0 50 197 52 31 . W 0 50 450 52 30 . W 0 50 S find 52 55 . W 0 50 146 52 163 . W 0 50 S convenient 52 165 . W 0 50 54 52 392 . W 0 50 254 52 216 . W 0 50 S ALT+. 52 100 . W 0 50 54 52 173 . W 0 50 266 52 286 . W 0 50 233 52 109 . W 0 50 258 52 178 . 59 4 . P 36 W 0 50 411 52 31 . W 0 50 197 52 15 . W 0 50 S see 52 301 . W 0 50 69 52 58 . W 0 50 316 52 163 . W 0 50 S combination 52 73 . W 0 50 54 52 369 . W 0 50 233 52 79 . W 0 50 235 52 82 . W 0 50 186 52 137 . W 0 50 69 52 338 . W 0 50 324 52 85 . W 0 50 S item, 52 178 . W 0 50 S that 52 113 . W 0 50 S means 52 294 . W 0 50 S it's 52 153 . W 0 50 69 52 N 74 . W 0 50 S shortcut. 52 31 . W 0 50 260 52 238 . W 0 50 233 52 165 . W 0 50 316 52 274 . W 0 50 504 52 277 . W 0 50 S without 52 82 . W 0 50 S opening 52 106 . W 0 50 233 52 178 . W 0 50 324 52 292 . W 0 50 105 52 115 . W 0 50 514 52 117 . W 0 50 283 52 187 . W 0 50 425 52 31 . W 0 50 452 52 160 . W 0 50 S invoked. 52 55 . W 0 50 S They're 52 73 . W 0 50 S configurable. 52 309 . W 0 50 S See 52 135 . W 0 50 S below. 52 286 . 59 4 . P 41 W 0 50 305 52 31 . W 0 50 276 52 30 . W 0 50 S load 52 55 . W 0 50 105 52 163 . W 0 50 S save 52 73 . W 0 50 S files 52 76 . W 0 50 288 52 96 . W 0 50 233 52 100 . W 0 50 430 52 103 . W 0 50 258 52 286 . W 0 50 S A 52 143 . W 0 50 S Document 52 256 . W 0 50 S Set 52 N 60 . W 0 50 66 52 N 64 . W 0 50 69 52 120 . W 0 50 416 52 153 . W 0 50 476 52 520 . W 0 50 S disk. 52 31 . W 0 50 S Each 52 460 . W 0 50 416 52 58 . W 0 50 276 52 165 . W 0 50 S contain 52 274 . W 0 50 S multiple 52 96 . W 0 50 S Documents. 52 334 . W 0 50 S Pay 52 109 . W 0 50 S careful 52 178 . W 0 50 S attention 52 568 . W 0 50 54 52 187 . W 0 50 S whether 52 31 . W 0 50 57 52 55 . W 0 50 66 52 274 . W 0 50 S talking 52 130 . W 0 50 397 52 98 . W 0 50 S Documents 52 334 . W 0 50 300 52 85 . W 0 50 S Document 52 340 . W 0 50 S Sets. 52 147 . W 0 50 S There's 52 117 . W 0 50 S more 52 190 . W 0 50 S information 52 31 . W 0 50 547 52 67 . 59 4 . P 68 W 0 50 S Some 52 31 . W 0 50 S operations 52 160 . W 0 50 S (applying 52 165 . W 0 50 S character 52 169 . W 0 50 S styles, 52 82 . W 0 50 S cut 52 106 . W 0 50 105 52 109 . W 0 50 S paste) 52 178 . W 0 50 S require 52 147 . W 0 50 197 52 120 . W 0 50 54 52 264 . W 0 50 279 52 31 . W 0 50 177 52 301 . W 0 50 S To 52 163 . W 0 50 139 52 165 . W 0 50 S this, 52 93 . W 0 50 S move 52 309 . W 0 50 54 52 216 . W 0 50 366 52 394 . W 0 50 S end 52 334 . W 0 50 186 52 471 . W 0 50 233 52 106 . W 0 50 S area 52 109 . W 0 50 54 52 226 . W 0 50 S mark; 52 292 . W 0 50 311 52 150 . W 0 50 S CTRL+SPACE; 52 31 . W 0 50 642 52 67 . W 0 50 54 52 73 . W 0 50 233 52 274 . W 0 50 S other 52 277 . W 0 50 S end. 52 216 . W 0 50 S The 52 219 . W 0 50 S operation 52 135 . W 0 50 425 52 143 . W 0 50 452 52 342 . W 0 50 S applied 52 344 . W 0 50 54 52 117 . W 0 50 233 52 296 . W 0 50 314 52 31 . W 0 50 50 52 67 . W 0 50 S between 52 73 . W 0 50 233 52 309 . W 0 50 S mark 52 369 . W 0 50 105 52 100 . W 0 50 233 52 103 . W 0 50 S cursor. 52 137 . W 0 50 S Note 52 340 . W 0 50 514 52 111 . W 0 50 233 52 568 . W 0 50 S marks 52 570 . W 0 50 S are 52 187 . W 0 50 S always 52 31 . W 0 50 S placed 52 301 . W 0 50 476 52 125 . W 0 50 233 52 73 . W 0 50 S left 52 128 . W 0 50 S side 52 169 . W 0 50 186 52 98 . W 0 50 233 52 100 . W 0 50 684 52 103 . W 0 50 S Pressing 52 140 . W 0 50 S CTRL+SPACE 52 111 . W 0 50 S again 52 184 . W 0 50 425 52 231 . W 0 50 S remove 52 31 . W 0 50 233 52 301 . W 0 50 S marks. 52 58 . 59 4 . P 7 W 0 50 S That's 52 31 . W 0 50 268 52 301 . W 0 50 S Now 52 58 . W 0 50 197 52 70 . W 0 50 276 52 93 . W 0 50 S work 52 130 . W 0 50 S WordGrinder. 52 96 . 59 4 . P 1 W 0 50 S Styles 52 31 . 59 18 . P 6 W 0 50 57 52 31 . W 0 50 S supports 52 67 . W 0 50 233 52 128 . W 0 50 S following 52 309 . W 0 50 622 52 219 . W 0 50 S styles: 52 338 . 59 4 . P 2 W 0 50 S Italicised 52 31 . W 0 50 S text 52 58 . 59 35 . P 2 W 0 50 S Underlined 52 31 . W 0 50 S text 52 58 . 59 35 . P 31 W 0 50 720 52 31 . W 0 50 268 52 301 . W 0 50 305 52 58 . W 0 50 276 52 70 . W 0 50 254 52 93 . W 0 50 S combinations 52 130 . W 0 50 S if 52 82 . W 0 50 197 52 135 . W 0 50 S like. 52 286 . W 0 50 637 52 143 . W 0 50 S apply 52 178 . W 0 50 S them, 52 113 . W 0 50 279 52 570 . W 0 50 445 52 264 . W 0 50 50 52 31 . W 0 50 105 52 160 . W 0 50 S then 52 205 . W 0 50 254 52 125 . W 0 50 S Style▷Italic 52 387 . W 0 50 S (CTRL+I) 52 98 . W 0 50 300 52 173 . W 0 50 S Style▷Underline 52 286 . W 0 50 S (CTRL+B) 52 147 . W 0 50 54 52 296 . W 0 50 S apply. 52 264 . W 0 50 637 52 31 . W 0 50 714 52 15 . W 0 50 624 52 238 . W 0 50 254 52 387 . W 0 50 S Style▷Plain 52 76 . W 0 52 100 50 S (CTRL+O). . 59 4 . P 39 W 0 52 31 50 S Tip: . W 0 52 160 50 54 . W 0 52 55 50 225 . W 0 52 163 50 69 . W 0 52 70 50 S new . W 0 52 93 50 72 . W 0 52 277 50 S in . W 0 52 392 50 69 . W 0 52 369 50 S particular . W 0 52 173 50 622 . W 0 52 340 50 S style, . W 0 52 344 50 S rather . W 0 52 570 50 S than . W 0 52 153 50 S changing . W 0 52 31 50 233 . W 0 52 30 50 59 . W 0 52 238 50 186 . W 0 52 163 50 S existing . W 0 52 76 50 S text, . W 0 52 96 50 197 . W 0 52 79 50 276 . W 0 52 82 50 139 . W 0 52 135 50 S CTRL+SPACE, . W 0 52 256 50 S followed . W 0 52 568 50 285 . W 0 52 294 50 233 . W 0 52 120 50 50 . W 0 52 231 50 197 . W 0 52 31 50 251 . W 0 52 160 50 54 . W 0 52 55 50 768 . W 0 52 125 50 233 . W 0 52 387 50 59 . W 0 52 277 50 S to, . W 0 52 96 50 834 . W 0 52 334 50 285 . W 0 52 173 50 S CTRL+I . W 0 52 85 50 300 . W 0 50 S CTRL+B. 52 340 . 59 4 . P 50 W 0 50 S There 52 31 . W 0 50 S are, 52 460 . W 0 50 S however, 52 58 . W 0 50 69 52 274 . W 0 50 S number 52 76 . W 0 50 186 52 369 . W 0 50 S paragraph 52 79 . W 0 50 2 52 137 . W 0 50 S supported. 52 143 . W 0 50 545 52 568 . W 0 50 S Style▷Paragraph 52 31 . W 0 50 S Styles 52 165 . W 0 50 S (CTRL+P) 52 130 . W 0 50 78 52 79 . W 0 50 233 52 82 . W 0 50 S list. 52 173 . W 0 50 577 52 140 . W 0 50 59 52 256 . W 0 50 S applies 52 344 . W 0 50 54 52 117 . W 0 50 69 52 296 . W 0 50 S complete 52 31 . W 0 50 S paragraph. 52 205 . W 0 50 411 52 274 . W 0 50 197 52 130 . W 0 50 251 52 392 . W 0 50 54 52 79 . W 0 50 500 52 219 . W 0 50 S what 52 135 . W 0 50 59 52 106 . W 0 50 S each 52 340 . W 0 50 865 52 111 . W 0 50 S has 52 117 . W 0 50 S set, 52 153 . W 0 50 254 52 520 . W 0 50 S Style▷Set 52 31 . W 0 50 S Margin 52 238 . W 0 50 S Mode▷Show 52 73 . W 0 50 S Paragraph 52 392 . W 0 50 S Styles. 52 334 . W 0 50 665 52 338 . W 0 50 59 52 143 . W 0 50 S names 52 111 . W 0 50 425 52 115 . W 0 50 452 52 184 . W 0 50 S displayed 52 31 . W 0 50 808 52 238 . W 0 50 233 52 163 . W 0 50 S left-hand 52 73 . W 0 50 S margin. 52 392 . 59 4 . P 17 W 0 50 S Paragraph 52 31 . W 0 50 2 52 238 . W 0 50 692 52 73 . W 0 50 90 52 128 . W 0 50 S currently 52 309 . W 0 50 S customisable, 52 219 . W 0 50 401 52 143 . W 0 50 146 52 226 . W 0 50 66 52 292 . W 0 50 S intended 52 147 . W 0 50 514 52 296 . W 0 50 S they 52 190 . W 0 50 425 52 31 . W 0 50 452 52 160 . W 0 50 808 52 55 . W 0 50 233 52 58 . W 0 50 S future. 52 70 . 59 4 . P 4 W 0 50 601 52 31 . W 0 50 105 52 238 . W 0 50 604 52 125 . W 0 50 S Sets 52 130 . 59 18 . P 29 W 0 50 577 52 31 . W 0 50 57 52 160 . W 0 50 416 52 73 . W 0 50 276 52 76 . W 0 50 581 52 169 . W 0 50 583 52 100 . W 0 50 S pieces 52 286 . W 0 50 186 52 340 . W 0 50 177 52 226 . W 0 50 563 52 147 . W 0 50 416 52 115 . W 0 50 66 52 184 . W 0 50 S referred 52 153 . W 0 50 54 52 31 . W 0 50 S as 52 15 . W 0 50 69 52 460 . W 0 50 565 52 55 . W 0 50 S Set; 52 73 . W 0 50 899 52 76 . W 0 50 S piece 52 392 . W 0 50 186 52 394 . W 0 50 50 52 82 . W 0 50 S within 52 471 . W 0 50 233 52 109 . W 0 50 604 52 178 . W 0 50 S Set 52 115 . W 0 50 66 52 117 . W 0 50 69 52 296 . W 0 50 S Document. 52 31 . 59 4 . P 39 W 0 50 305 52 31 . W 0 50 276 52 30 . W 0 50 406 52 55 . W 0 50 978 52 163 . W 0 50 S many 52 165 . W 0 50 45 52 128 . W 0 50 808 52 98 . W 0 50 69 52 100 . W 0 50 S document 52 82 . W 0 50 S set 52 338 . W 0 50 978 52 143 . W 0 50 197 52 178 . W 0 50 S like 52 292 . W 0 50 S (within 52 115 . W 0 50 S reason...). 52 31 . W 0 50 305 52 67 . W 0 50 276 52 165 . W 0 50 S manage 52 274 . W 0 50 S them 52 392 . W 0 50 288 52 79 . W 0 50 233 52 103 . W 0 50 S entries 52 137 . W 0 50 808 52 340 . W 0 50 233 52 226 . W 0 50 430 52 344 . W 0 50 258 52 150 . W 0 50 305 52 296 . W 0 50 276 52 231 . W 0 50 500 52 31 . W 0 50 105 52 30 . W 0 50 S switch 52 55 . W 0 50 677 52 70 . W 0 50 45 52 130 . W 0 50 288 52 394 . W 0 50 233 52 135 . W 0 50 601 52 286 . W 0 50 324 52 226 . W 0 50 S (ESC, 52 113 . W 0 50 S D). 52 570 . 59 4 . P 37 W 0 50 601 52 31 . W 0 50 406 52 238 . W 0 50 S their 52 70 . W 0 50 S own 52 128 . W 0 50 827 52 309 . W 0 50 S selection, 52 98 . W 0 50 S viewing 52 137 . W 0 50 814 52 340 . W 0 50 291 52 344 . W 0 50 S position, 52 570 . W 0 50 S etc. 52 31 . W 0 50 665 52 160 . W 0 50 S clipboard, 52 205 . W 0 50 859 52 274 . W 0 50 66 52 369 . W 0 50 S shared 52 79 . W 0 50 S across 52 135 . W 0 50 233 52 140 . W 0 50 S entire 52 340 . W 0 50 1009 52 344 . W 0 50 S set. 52 184 . W 0 50 S This 52 264 . W 0 50 S allows 52 31 . W 0 50 197 52 301 . W 0 50 54 52 58 . W 0 50 642 52 125 . W 0 50 968 52 93 . W 0 50 186 52 169 . W 0 50 50 52 369 . W 0 50 S around 52 100 . W 0 50 989 52 471 . W 0 50 233 52 109 . W 0 50 1009 52 178 . W 0 50 1011 52 115 . W 0 50 288 52 117 . W 0 50 233 52 264 . W 0 50 S clipboard. 52 31 . 59 4 . P 16 W 0 50 S Importing 52 31 . W 0 50 S non-WordGrinder 52 238 . W 0 50 557 52 169 . W 0 50 694 52 79 . W 0 50 S results 52 135 . W 0 50 808 52 85 . W 0 50 69 52 340 . W 0 50 S new 52 178 . W 0 50 1009 52 292 . W 0 50 S being 52 117 . W 0 50 S added 52 264 . W 0 50 54 52 31 . W 0 50 233 52 15 . W 0 50 S current 52 301 . W 0 50 1009 52 70 . W 0 50 1082 52 277 . 59 4 . P 2 W 0 52 31 50 S Menu . W 0 52 160 50 S customisation . 59 18 . P 32 W 0 52 31 50 305 . W 0 52 30 50 276 . W 0 52 55 50 S change . W 0 52 70 50 S any . W 0 52 93 50 186 . W 0 52 76 50 233 . W 0 52 169 50 S keyboard . W 0 52 219 50 S shortcuts . W 0 52 338 50 S assigned . W 0 52 342 50 54 . W 0 52 344 50 324 . W 0 52 150 50 S items. . W 0 52 153 50 S These . W 0 52 31 50 692 . W 0 52 30 50 776 . W 0 52 205 50 S saved . W 0 52 70 50 978 . W 0 52 387 50 S part . W 0 52 130 50 186 . W 0 52 169 50 233 . W 0 52 216 50 1009 . W 0 52 135 50 904 . W 0 52 106 50 S so . W 0 52 85 50 899 . W 0 52 178 50 57 . W 0 52 570 50 416 . W 0 52 153 50 S takes . W 0 52 31 50 S its . W 0 52 30 50 1142 . W 0 52 163 50 S configuration . W 0 52 392 50 S with . W 0 52 79 50 268 . 59 4 . P 79 W 0 52 31 50 637 . W 0 52 15 50 139 . W 0 52 460 50 640 . W 0 52 67 50 266 . W 0 52 73 50 233 . W 0 52 128 50 418 . W 0 52 392 50 S navigate . W 0 52 82 50 54 . W 0 52 135 50 233 . W 0 52 286 50 S entry . W 0 52 143 50 197 . W 0 52 226 50 251 . W 0 52 113 50 54 . W 0 52 115 50 S change, . W 0 52 153 50 105 . W 0 52 190 50 776 . W 0 52 31 50 311 . W 0 52 460 50 S CTRL+V . W 0 52 163 50 834 . W 0 52 76 50 285 . W 0 52 309 50 233 . W 0 52 369 50 316 . W 0 52 394 50 197 . W 0 52 334 50 251 . W 0 52 137 50 54 . W 0 52 338 50 S bind. . W 0 52 256 50 305 . W 0 52 111 50 391 . W 0 52 115 50 S bind . W 0 52 184 50 233 . W 0 52 187 50 50 . W 0 52 31 50 1191 . W 0 52 460 50 S keys. . W 0 52 67 50 305 . W 0 52 165 50 308 . W 0 52 128 50 391 . W 0 52 392 50 1214 . W 0 52 79 50 69 . W 0 52 100 50 316 . W 0 52 103 50 S that's . W 0 52 338 50 S already . W 0 52 226 50 808 . W 0 52 292 50 254 . W 0 52 568 50 S elsewhere. . W 0 52 264 50 305 . W 0 52 31 50 276 . W 0 52 30 50 714 . W 0 52 58 50 69 . W 0 52 163 50 S binding . W 0 52 128 50 285 . W 0 52 277 50 S navigating . W 0 52 219 50 54 . W 0 52 103 50 233 . W 0 52 137 50 S appropriate . W 0 52 342 50 324 . W 0 52 147 50 283 . W 0 52 570 50 105 . W 0 52 296 50 375 . W 0 52 31 50 S CTRL+X. . W 0 52 55 50 305 . W 0 52 67 50 406 . W 0 52 73 50 54 . W 0 52 274 50 S manually . W 0 52 369 50 714 . W 0 52 82 50 69 . W 0 52 103 50 1239 . W 0 52 140 50 S from . W 0 52 256 50 69 . W 0 52 226 50 324 . W 0 52 113 50 283 . W 0 52 294 50 378 . W 0 52 187 50 197 . W 0 52 520 50 276 . W 0 52 31 50 1214 . W 0 52 160 50 69 . W 0 52 301 50 S different . W 0 52 73 50 316 . W 0 52 128 50 54 . W 0 52 277 50 268 . 59 4 . P 28 W 0 52 31 50 208 . W 0 52 30 50 57 . W 0 52 165 50 S keybindings . W 0 52 96 50 S (except . W 0 52 82 50 421 . W 0 52 471 50 105 . W 0 52 338 50 324 . W 0 52 340 50 105 . W 0 52 342 50 S dialogue . W 0 52 294 50 S navigation) . W 0 52 31 50 434 . W 0 52 301 50 476 . W 0 52 238 50 233 . W 0 52 125 50 S menus, . W 0 52 128 50 1165 . W 0 52 277 50 S they're . W 0 52 79 50 S all . W 0 52 82 50 543 . W 0 52 340 50 S Things . W 0 52 344 50 1015 . W 0 52 150 50 233 . W 0 52 184 50 291 . W 0 52 190 50 215 . W 0 52 31 50 434 . W 0 52 301 50 808 . W 0 52 238 50 233 . W 0 52 125 50 S Navigation . W 0 52 309 50 258 . 59 4 . P 18 W 0 52 31 50 411 . W 0 52 15 50 197 . W 0 52 301 50 S get . W 0 52 58 50 S irrecoverably . W 0 52 309 50 S confused, . W 0 52 219 50 197 . W 0 52 135 50 276 . W 0 52 286 50 S reset . W 0 52 143 50 1300 . W 0 52 226 50 233 . W 0 52 344 50 1280 . W 0 52 153 50 285 . W 0 52 31 50 530 . W 0 52 55 50 69 . W 0 52 238 50 324 . W 0 52 70 50 105 . W 0 52 93 50 243 . W 0 50 S CTRL+R. 52 309 . 59 4 . P 1 W 0 50 1105 52 31 . 59 18 . P 50 W 0 50 411 52 31 . W 0 50 197 52 15 . W 0 50 404 52 301 . W 0 50 251 52 163 . W 0 50 54 52 387 . W 0 50 S write 52 128 . W 0 50 69 52 392 . W 0 50 1009 52 369 . W 0 50 1262 52 103 . W 0 50 S scratch 52 286 . W 0 50 808 52 256 . W 0 50 S WordGrinder, 52 342 . W 0 50 518 52 120 . W 0 50 S possible 52 31 . W 0 50 54 52 205 . W 0 50 S import 52 67 . W 0 50 45 52 93 . W 0 50 808 52 369 . W 0 50 69 52 79 . W 0 50 S small 52 100 . W 0 50 862 52 173 . W 0 50 186 52 85 . W 0 50 661 52 340 . W 0 50 S formats 52 292 . W 0 50 288 52 570 . W 0 50 S File▷Import 52 31 . W 0 50 805 52 67 . W 0 50 S document. 52 165 . W 0 50 1152 52 169 . W 0 50 1300 52 79 . W 0 50 S add 52 82 . W 0 50 69 52 173 . W 0 50 805 52 137 . W 0 50 1009 52 140 . W 0 50 54 52 111 . W 0 50 233 52 113 . W 0 50 825 52 150 . W 0 50 1009 52 31 . W 0 50 904 52 205 . W 0 50 816 52 125 . W 0 50 818 52 128 . W 0 50 S replacing 52 169 . W 0 50 233 52 82 . W 0 50 1124 52 173 . W 0 50 S one. 52 109 . W 0 50 665 52 226 . W 0 50 S followng 52 344 . W 0 50 1371 52 184 . W 0 50 692 52 520 . W 0 50 S available: 52 31 . 59 4 . P 2 W 0 50 S Plain 52 31 . W 0 50 50 52 460 . 59 35 . P 17 W 0 50 S Imports 52 31 . W 0 50 S flat 52 55 . W 0 50 S UTF-8 52 163 . W 0 50 S encoded 52 93 . W 0 50 S plain 52 392 . W 0 50 50 52 394 . W 0 50 1177 52 103 . W 0 50 S no 52 286 . W 0 50 622 52 140 . W 0 50 300 52 292 . W 0 50 865 52 147 . W 0 50 155 52 31 . W 0 50 S Paragraphs 52 55 . W 0 50 692 52 93 . W 0 50 S delimited 52 130 . W 0 50 285 52 394 . W 0 50 S newlines. 52 82 . 59 41 . P 1 W 0 50 S HTML 52 31 . 59 35 . P 42 W 0 50 1411 52 31 . W 0 50 S 'tag 52 55 . W 0 50 S soup' 52 163 . W 0 50 1415 52 93 . W 0 50 1417 52 309 . W 0 50 S HTML. 52 394 . W 0 50 208 52 135 . W 0 50 S unknown 52 286 . W 0 50 S tags 52 256 . W 0 50 692 52 292 . W 0 50 S ignored. 52 568 . W 0 50 S 52 153 . W 0 50 S (or 52 31 . W 0 50 S ) 52 30 . W 0 50 105 52 238 . W 0 50 S 52 125 . W 0 50 692 52 387 . W 0 50 S mapped 52 76 . W 0 50 54 52 369 . W 0 50 S italic 52 79 . W 0 50 105 52 135 . W 0 50 S underline, 52 286 . W 0 50 S respectively; 52 342 . W 0 50 S

    ..

    52 31 . W 0 50 S become 52 58 . W 0 50 865 52 387 . W 0 50 2 52 96 . W 0 50 16 52 219 . W 0 50 54 52 103 . W 0 50 S H4; 52 471 . W 0 50 1300 52 338 . W 0 50 S
  • 52 143 . W 0 50 1480 52 342 . W 0 50 S LB. 52 115 . W 0 50 1429 52 31 . W 0 50 692 52 58 . W 0 50 1432 52 70 . W 0 50 285 52 309 . W 0 50 S either 52 96 . W 0 50 S

    52 219 . W 0 50 300 52 135 . W 0 50 S
    . 52 137 . 59 41 . P 2 W 0 50 S Exporting 52 31 . W 0 50 S 52 238 . 59 18 . P 31 W 0 50 270 52 31 . W 0 50 474 52 160 . W 0 50 S done 52 67 . W 0 50 1177 52 73 . W 0 50 189 52 76 . W 0 50 S document, 52 392 . W 0 50 197 52 334 . W 0 50 276 52 471 . W 0 50 S export 52 338 . W 0 50 146 52 178 . W 0 50 54 52 111 . W 0 50 69 52 113 . W 0 50 1366 52 568 . W 0 50 401 52 184 . W 0 50 S growing 52 187 . W 0 50 S variety 52 31 . W 0 50 186 52 55 . W 0 50 661 52 58 . W 0 50 1371 52 73 . W 0 50 288 52 309 . W 0 50 233 52 98 . W 0 50 S File▷Export 52 219 . W 0 50 S current 52 85 . W 0 50 S document 52 111 . W 0 50 324 52 570 . W 0 50 S option. 52 153 . W 0 50 665 52 31 . W 0 50 739 52 30 . W 0 50 1371 52 125 . W 0 50 692 52 76 . W 0 50 1404 52 169 . 59 4 . P 2 W 0 50 1407 52 31 . W 0 50 50 52 460 . 59 35 . P 35 W 0 50 1084 52 31 . W 0 50 S produces 52 160 . W 0 50 69 52 125 . W 0 50 S simple, 52 165 . W 0 50 1413 52 277 . W 0 50 1415 52 369 . W 0 50 50 52 219 . W 0 50 S file. 52 173 . W 0 50 S Character 52 140 . W 0 50 105 52 292 . W 0 50 865 52 568 . W 0 50 2 52 31 . W 0 50 692 52 301 . W 0 50 S lost, 52 58 . W 0 50 401 52 73 . W 0 50 1300 52 128 . W 0 50 189 52 309 . W 0 50 50 52 216 . W 0 50 S remains. 52 219 . W 0 50 1429 52 106 . W 0 50 692 52 111 . W 0 50 1432 52 147 . W 0 50 285 52 153 . W 0 50 S newlines, 52 31 . W 0 50 1165 52 238 . W 0 50 197 52 163 . W 0 50 646 52 73 . W 0 50 462 52 128 . W 0 50 1177 52 277 . W 0 50 366 52 369 . W 0 50 S very 52 394 . W 0 50 S long 52 103 . W 0 50 S line 52 286 . W 0 50 S per 52 109 . W 0 50 889 52 178 . 59 41 . P 1 W 0 50 1438 52 31 . 59 35 . P 23 W 0 50 1084 52 31 . W 0 50 S generates 52 160 . W 0 50 69 52 70 . W 0 50 1592 52 73 . W 0 50 S basic 52 76 . W 0 50 1415 52 96 . W 0 50 1417 52 100 . W 0 50 1438 52 137 . W 0 50 1517 52 85 . W 0 50 S nominally 52 344 . W 0 50 1438 52 120 . W 0 50 S 4. 52 31 . W 0 50 S Practically 52 15 . W 0 50 S everything 52 70 . W 0 50 425 52 169 . W 0 50 S read 52 98 . W 0 50 S this. 52 82 . W 0 50 1566 52 137 . W 0 50 105 52 178 . W 0 50 865 52 292 . W 0 50 2 52 184 . W 0 50 692 52 31 . W 0 50 S exported. 52 30 . 59 41 . P 1 W 0 50 S LaTeX 52 31 . 59 35 . P 65 W 0 50 1084 52 31 . W 0 50 1605 52 160 . W 0 50 69 52 70 . W 0 50 1415 52 73 . W 0 50 1637 52 130 . W 0 50 1009 52 369 . W 0 50 S suitable 52 103 . W 0 50 78 52 85 . W 0 50 S running 52 256 . W 0 50 S through 52 147 . W 0 52 120 50 S XeTeX . W 0 52 31 50 288 . W 0 52 460 50 233 . W 0 52 238 50 S 'xelatex' . W 0 52 274 50 S command. . W 0 52 369 50 305 . W 0 52 394 50 276 . W 0 52 334 50 254 . W 0 52 471 50 S stock . W 0 52 85 50 1637 . W 0 52 226 50 762 . W 0 52 292 50 197 . W 0 52 568 50 S wish, . W 0 52 184 50 401 . W 0 52 187 50 197 . W 0 52 31 50 425 . W 0 52 160 50 S need . W 0 52 238 50 54 . W 0 52 163 50 S edit . W 0 52 387 50 233 . W 0 52 76 50 S resulting . W 0 52 79 50 416 . W 0 52 334 50 54 . W 0 52 173 50 714 . W 0 52 85 50 233 . W 0 52 256 50 S xunicode . W 0 52 568 50 S package . W 0 52 296 50 S --- . W 0 52 31 50 401 . W 0 52 30 50 452 . W 0 52 301 50 S aware . W 0 52 163 50 514 . W 0 52 387 50 1138 . W 0 52 76 50 S non-ASCII . W 0 52 79 50 S characters . W 0 52 286 50 S probably . W 0 52 178 50 S won't . W 0 52 113 50 727 . W 0 52 294 50 S correctly. . W 0 52 31 50 1566 . W 0 52 238 50 105 . W 0 52 125 50 865 . W 0 52 277 50 2 . W 0 52 98 50 692 . W 0 52 219 50 S supported; . W 0 52 140 50 S H1..H4 . W 0 52 226 50 692 . W 0 52 344 50 1468 . W 0 52 570 50 54 . W 0 52 31 50 S \section, . W 0 52 238 50 S \subsection, . W 0 52 130 50 S \subsubsection . W 0 52 103 50 105 . W 0 52 137 50 S \paragraph . W 0 50 S respectively. 52 226 . 59 41 . P 41 W 0 50 S However, 52 31 . W 0 50 S bear 52 205 . W 0 50 808 52 125 . W 0 50 S mind 52 73 . W 0 50 514 52 76 . W 0 50 464 52 392 . W 0 50 S when 52 79 . W 0 50 288 52 334 . W 0 50 S XeTeX, 52 286 . W 0 50 S TeX's 52 340 . W 0 50 S Unicode 52 292 . W 0 50 S support 52 570 . W 0 50 66 52 31 . W 0 50 937 52 15 . W 0 50 816 52 163 . W 0 50 S poor, 52 274 . W 0 50 105 52 169 . W 0 50 514 52 216 . W 0 50 S which 52 219 . W 0 50 1700 52 471 . W 0 50 692 52 178 . W 0 50 S supported 52 292 . W 0 50 S depends 52 184 . W 0 50 S strongly 52 31 . W 0 50 476 52 205 . W 0 50 896 52 67 . W 0 50 S font 52 73 . W 0 50 474 52 76 . W 0 50 S using: 52 369 . W 0 50 S try 52 82 . W 0 50 S exporting 52 173 . W 0 50 S this 52 340 . W 0 50 1009 52 111 . W 0 50 105 52 570 . W 0 50 776 52 296 . W 0 50 S looking 52 31 . W 0 50 S at 52 55 . W 0 50 233 52 58 . W 0 52 70 50 S Unicode . W 0 50 S chapter 52 130 . W 0 52 98 50 547 . 59 41 . P 1 W 0 52 31 50 S Troff . 59 35 . P 44 W 0 52 31 50 1084 . W 0 52 160 50 1605 . W 0 52 70 50 281 . W 0 52 387 50 S ASCII . W 0 52 277 50 1797 . W 0 52 216 50 416 . W 0 52 219 50 1646 . W 0 52 106 50 78 . W 0 52 109 50 254 . W 0 52 178 50 1177 . W 0 52 344 50 233 . W 0 52 115 50 S ms . W 0 52 570 50 S macro . W 0 52 31 50 S package. . W 0 52 205 50 1566 . W 0 52 93 50 105 . W 0 52 130 50 865 . W 0 52 394 50 2 . W 0 52 173 50 692 . W 0 52 106 50 1714 . W 0 52 111 50 1779 . W 0 52 117 50 54 . W 0 52 296 50 S troff . W 0 52 31 50 105 . W 0 52 30 50 776 . W 0 52 205 50 1649 . W 0 52 73 50 233 . W 0 52 128 50 S result . W 0 52 96 50 1651 . W 0 52 82 50 S nroff . W 0 52 137 50 66 . W 0 52 338 50 69 . W 0 52 85 50 S good . W 0 52 178 50 192 . W 0 52 292 50 186 . W 0 52 147 50 S getting . W 0 52 120 50 69 . W 0 52 31 50 S formatted . W 0 52 238 50 1419 . W 0 52 165 50 50 . W 0 52 128 50 S version . W 0 52 369 50 186 . W 0 52 79 50 69 . W 0 52 100 50 S document. . 59 41 . P 79 W 0 52 31 50 S Non-ASCII . W 0 52 238 50 1700 . W 0 52 128 50 692 . W 0 52 309 50 1417 . W 0 52 394 50 288 . W 0 52 135 50 233 . W 0 52 286 50 S \[char1234] . W 0 52 111 50 S mechanism, . W 0 52 184 50 1760 . W 0 52 31 50 516 . W 0 52 460 50 514 . W 0 52 58 50 762 . W 0 52 125 50 474 . W 0 52 128 50 S lucky . W 0 52 392 50 S enough . W 0 52 100 50 54 . W 0 52 334 50 406 . W 0 52 137 50 69 . W 0 52 106 50 1850 . W 0 52 178 50 186 . W 0 52 111 50 S groff . W 0 52 115 50 S modern . W 0 52 31 50 1875 . W 0 52 301 50 54 . W 0 52 238 50 S handle . W 0 52 73 50 S proper . W 0 52 277 50 S Unicode, . W 0 52 394 50 146 . W 0 52 82 50 218 . W 0 52 286 50 1300 . W 0 52 85 50 221 . W 0 52 226 50 411 . W 0 52 292 50 197 . W 0 52 568 50 S don't, . W 0 52 120 50 776 . W 0 50 1700 52 31 . W 0 50 808 52 58 . W 0 50 233 52 125 . W 0 50 S ISO-8859-1 52 387 . W 0 50 S range 52 369 . W 0 50 425 52 219 . W 0 50 1702 52 173 . W 0 50 S work, 52 143 . W 0 50 105 52 111 . W 0 50 1300 52 147 . W 0 50 S others 52 294 . W 0 50 425 52 31 . W 0 50 452 52 160 . W 0 50 1456 52 55 . W 0 50 S (I 52 73 . W 0 50 404 52 274 . W 0 50 254 52 169 . W 0 50 233 52 216 . W 0 50 S \[u1234] 52 100 . W 0 50 S idiom 52 286 . W 0 50 S because 52 143 . W 0 50 1781 52 344 . W 0 50 S causes 52 150 . W 0 50 1882 52 31 . W 0 50 S 1.18.1 52 460 . W 0 50 54 52 163 . W 0 50 S crash 52 165 . W 0 50 S horribly 52 76 . W 0 50 1742 52 98 . W 0 50 1779 52 82 . W 0 50 1781 52 140 . W 0 50 1854 52 256 . W 0 50 S Blame 52 115 . W 0 50 233 52 120 . W 0 50 1882 52 31 . W 0 50 S team; 52 460 . W 0 50 S they've 52 67 . W 0 50 S been 52 274 . W 0 50 S promising 52 309 . W 0 50 S real 52 219 . W 0 50 1415 52 173 . W 0 50 78 52 140 . W 0 50 S years 52 340 . W 0 50 S now.) 52 292 . 59 41 . P 1 W 0 50 1749 52 31 . 59 18 . P 3 W 0 52 31 50 57 . W 0 52 67 50 736 . W 0 52 128 50 S UNICODE! . 59 4 . P 50 W 0 52 31 50 S Mostly, . W 0 52 55 50 1788 . W 0 52 58 50 1138 . W 0 52 70 50 S rate. . W 0 52 128 50 S Combining . W 0 52 98 50 1700 . W 0 52 137 50 105 . W 0 52 140 50 S right-to-left . W 0 52 568 50 692 . W 0 52 570 50 S unsupported. . W 0 52 31 50 1084 . W 0 52 160 50 516 . W 0 52 58 50 514 . W 0 52 165 50 197 . W 0 52 274 50 276 . W 0 52 277 50 S use, . W 0 52 369 50 S say, . W 0 52 100 50 S Français, . W 0 52 106 50 S русский . W 0 52 178 50 S язык, . W 0 52 113 50 S 한국어 . W 0 52 117 50 105 . W 0 52 153 50 S 日本語, . W 0 52 31 50 401 . W 0 52 30 50 197 . W 0 52 55 50 391 . W 0 52 125 50 254 . W 0 52 387 50 S Arabic . W 0 52 309 50 300 . W 0 52 96 50 S Hebrew . W 0 52 219 50 1930 . W 0 52 286 50 947 . W 0 52 109 50 646 . W 0 52 178 50 462 . W 0 52 111 50 S mangled . W 0 52 294 50 S (العربية . W 0 52 231 50 105 . W 0 52 31 50 S עברית . W 0 52 460 50 S respectively). . W 0 52 128 50 305 . W 0 52 309 50 276 . W 0 52 369 50 254 . W 0 52 394 50 1596 . W 0 52 103 50 S art . W 0 52 137 50 401 . W 0 52 140 50 1227 . W 0 52 226 50 90 . W 0 52 344 50 896 . W 0 52 150 50 S WordGrinder's . W 0 52 31 50 S for. . 59 4 . P 23 W 0 52 31 50 S Unfortunately, . W 0 52 70 50 S how . W 0 52 93 50 54 . W 0 52 76 50 382 . W 0 52 98 50 S enter . W 0 52 334 50 1749 . W 0 52 338 50 1700 . W 0 52 292 50 66 . W 0 52 147 50 S entirely . W 0 52 296 50 462 . W 0 52 264 50 54 . W 0 52 31 50 189 . W 0 52 160 50 S operating . W 0 52 70 50 S system . W 0 52 76 50 105 . W 0 52 169 50 189 . W 0 52 98 50 S terminal . W 0 52 173 50 S application. . W 0 52 226 50 57 . W 0 52 117 50 391 . W 0 52 264 50 S help . W 0 52 31 50 197 . W 0 52 30 50 S there. . 59 4 . S viewmode 8 S wordcount N 1328 S co 8 S cw 8 5 S README S cp 8 S margin 31 . 2084 47 . S statusbar B F 5 S README.wg S clipboard D 1 P 26 W 0 50 233 . W 0 50 1111 . W 0 50 276 . W 0 50 452 . W 0 50 816 . W 0 50 1756 . W 0 50 S including . W 0 50 1882 . W 0 50 S crashing: . W 0 50 1781 . W 0 50 S happens . W 0 50 54 . W 0 50 S me . W 0 50 1177 . W 0 50 1882 . W 0 50 1936 . W 0 50 1742 . W 0 50 S trying . W 0 50 54 . W 0 50 1521 . W 0 50 1781 . W 0 50 1854 . W 0 50 S Your . W 0 50 S Mileage . W 0 50 S May . W 0 50 S Vary. . 59 41 . 2079 8 2082 8 2085 8 2086 31 2083 8 . 1124 47 324 M 0 S accelerators T 0 1508 S SM S EC S ^C S DC S ZDNC S SS S ^W S ZU S UP S BACKSPACE S ZDPC 2141 2140 S SU S ^U 2135 2134 S ZM S ^@ S RIGHT S ZR S ^V S EP S FQ S ^Q S SO S ^O S ZE S END 2131 1508 S ^X S ET 2133 2132 S NPAGE S ZPGDN S FS S ^S S EF S ^F S ^P S SP S EN S ^K 2165 2164 2145 2144 2149 2148 S DOWN S ZD 2167 2166 S ZPGUP S PPAGE 2139 2138 2153 2152 2147 2146 2171 2170 2161 2160 S ZL S LEFT 2151 2150 2137 2136 S SB S ^I 2143 2142 S HOME S ZH 2177 2176 2155 2154 2173 2172 S ER S ^R 2175 2174 2163 2162 2157 2156 2169 2168 2179 2178 2159 2158 . . . wordgrinder-0.5.1.orig/testdocs/README-v0.3.3.wg0000644000000000000000000015741112121454253015651 0ustar WordGrinder dumpfile v1: this is not a text file! DS 0 S documents T 2 D 85 P 3 W 0 S text S Welcome S x N 0 . W 0 7 S to 9 N 8 . W 0 7 S WordGrinder 9 N 11 . S style T 0 S above N 3 S desc S Heading #1 S cstyle 20 S below N 1 S html S H1 S name 27 . . P 1 W 0 7 S Introduction 9 10 . 17 T 0 19 N 2 21 S Heading #2 23 20 24 25 26 S H2 28 35 . . P 8 W 0 7 15 9 10 . W 0 7 S is 9 N 12 . W 0 7 S a 9 N 15 . W 0 7 S word 9 N 17 . W 0 7 S processor 9 N 22 . W 0 7 S for 9 N 32 . W 0 7 S processing 9 N 36 . W 0 7 S words. 9 N 47 . 17 T 0 19 25 21 S Plain text 28 S P 26 61 24 25 . . P 32 W 0 7 15 9 10 . W 0 7 39 9 40 . W 0 7 S not 9 43 . W 0 7 S WYSIWYG. 9 N 19 . W 0 7 S It 9 N 28 . W 0 7 39 9 N 31 . W 0 7 66 9 N 34 . W 0 7 S point 9 N 38 . W 0 7 S and 9 N 44 . W 0 7 S click. 9 N 48 . W 0 7 71 9 N 55 . W 0 7 39 9 N 58 . W 0 7 66 9 N 61 . W 0 7 42 9 N 65 . W 0 7 S desktop 9 N 67 . W 0 7 S publisher. 9 10 . W 0 7 71 9 16 . W 0 7 39 9 N 14 . W 0 7 66 9 46 . W 0 7 42 9 N 21 . W 0 7 7 9 N 23 . W 0 7 S editor. 9 72 . W 0 7 71 9 55 . W 0 7 39 9 N 39 . W 0 7 66 9 N 42 . W 0 7 S do 9 N 46 . W 0 7 S fonts 9 N 49 . W 0 7 81 9 87 . W 0 7 S it 9 N 59 . W 0 7 S barely 9 N 62 . W 0 7 S does 9 N 69 . W 0 7 S styles. 9 10 . 17 59 . P 21 W 0 7 S What 9 10 . W 0 7 122 9 N 5 . W 0 7 128 9 13 . W 0 7 115 9 N 13 . W 0 7 39 9 N 16 . W 0 7 57 9 69 . W 0 7 S It's 9 N 26 . W 0 7 S designed 9 74 . W 0 7 51 9 N 40 . W 0 7 S writing 9 82 . W 0 7 S text. 9 N 52 . W 0 7 71 9 89 . W 0 7 S gets 9 91 . W 0 7 S out 9 N 66 . W 0 7 S of 9 N 70 . W 0 7 S your 9 N 73 . W 0 7 S way 9 10 . W 0 7 81 9 N 4 . W 0 7 S lets 9 13 . W 0 7 S you 9 139 . W 0 7 S type. 9 46 . 17 59 . P 2 W 0 7 S Survival 9 10 . W 0 7 S Guide 9 N 9 . 17 32 . P 16 W 0 7 S All 9 10 . W 0 7 165 9 170 . W 0 7 S favourite 9 182 . W 0 7 S navigation 9 69 . W 0 7 S keys 9 N 30 . W 0 7 S should 9 N 35 . W 0 7 S work. 9 113 . W 0 7 S Just 9 85 . W 0 7 S type 9 N 53 . W 0 7 81 9 89 . W 0 7 122 9 126 . W 0 7 195 9 93 . W 0 7 115 9 N 72 . W 0 7 S the 9 10 . W 0 7 S right 9 170 . W 0 7 S thing. 9 N 10 . 17 59 . P 63 W 0 7 S For 9 10 . W 0 7 S doing 9 170 . W 0 7 S anything 9 215 . W 0 7 S complicated, 9 69 . W 0 7 S you'll 9 52 . W 0 7 S want 9 111 . W 0 7 12 9 82 . W 0 7 S use 9 58 . W 0 7 210 9 N 51 . W 0 7 S menu. 9 87 . W 0 7 S Press 9 91 . W 0 7 S ESC 9 96 . W 0 7 12 9 N 71 . W 0 7 S open 9 10 . W 0 7 S it. 9 136 . W 0 7 S Once 9 182 . W 0 7 S open, 9 101 . W 0 7 174 9 N 20 . W 0 7 S can 9 N 24 . W 0 7 S select 9 72 . W 0 7 S an 9 196 . W 0 7 S item 9 79 . W 0 7 S by 9 N 43 . W 0 7 S using 9 116 . W 0 7 210 9 154 . W 0 7 S cursor 9 N 56 . W 0 7 192 9 N 63 . W 0 7 81 9 N 68 . W 0 7 S RETURN 9 10 . W 0 7 S or 9 N 7 . W 0 7 S RIGHT. 9 215 . W 0 7 S You 9 46 . W 0 7 253 9 104 . W 0 7 S also 9 N 25 . W 0 7 S press 9 193 . W 0 7 210 9 55 . W 0 7 S highlighted 9 149 . W 0 7 S key 9 154 . W 0 7 12 9 269 . W 0 7 210 9 123 . W 0 7 S left 9 271 . W 0 7 162 9 273 . W 0 7 210 9 241 . W 0 7 S menu 9 10 . W 0 7 260 9 136 . W 0 7 51 9 215 . W 0 7 S fast 9 101 . W 0 7 S navigation. 9 69 . W 0 7 237 9 74 . W 0 7 S LEFT 9 N 37 . W 0 7 12 9 113 . W 0 7 S back 9 N 45 . W 0 7 159 9 N 50 . W 0 7 162 9 N 54 . W 0 7 42 9 N 57 . W 0 7 S submenu. 9 123 . W 0 7 S Backing 9 273 . W 0 7 159 9 10 . W 0 7 162 9 170 . W 0 7 210 9 278 . W 0 7 S top-level 9 16 . W 0 7 301 9 104 . W 0 7 S cancels. 9 145 . 17 59 . P 83 W 0 7 S Most 9 10 . W 0 7 S platforms 9 136 . W 0 7 S enforce 9 43 . W 0 7 42 9 106 . W 0 7 S one 9 286 . W 0 7 S second 9 N 29 . W 0 7 S delay 9 55 . W 0 7 S after 9 113 . W 0 7 S pressing 9 85 . W 0 7 239 9 321 . W 0 7 S before 9 91 . W 0 7 210 9 273 . W 0 7 301 9 208 . W 0 7 S actually 9 10 . W 0 7 S appears. 9 182 . W 0 7 S Sorry, 9 N 18 . W 0 7 S I 9 286 . W 0 7 S can't 9 N 27 . W 0 7 115 9 N 33 . W 0 7 222 9 55 . W 0 7 S about 9 315 . W 0 7 S this... 9 233 . W 0 7 S but 9 123 . W 0 7 174 9 271 . W 0 7 S don't 9 96 . W 0 7 S have 9 166 . W 0 7 12 9 10 . W 0 7 S wait. 9 20 . W 0 7 S If 9 182 . W 0 7 174 9 40 . W 0 7 228 9 141 . W 0 7 210 9 104 . W 0 7 S file 9 286 . W 0 7 S menu, 9 193 . W 0 7 352 9 55 . W 0 7 S ESC, 9 315 . W 0 7 S F 9 317 . W 0 7 S will 9 154 . W 0 7 S cause 9 321 . W 0 7 210 9 271 . W 0 7 S File 9 96 . W 0 7 301 9 208 . W 0 7 12 9 10 . W 0 7 S appear 9 20 . W 0 7 210 9 215 . W 0 7 S instant 9 101 . W 0 7 174 9 49 . W 0 7 288 9 145 . W 0 7 S F. 9 52 . W 0 7 S On 9 196 . W 0 7 S most 9 79 . W 0 7 S platforms, 9 263 . W 0 7 174 9 319 . W 0 7 S may 9 89 . W 0 7 S be 9 126 . W 0 7 S able 9 93 . W 0 7 12 9 163 . W 0 7 S speed 9 10 . W 0 7 S things 9 N 6 . W 0 7 S up 9 139 . W 0 7 S even 9 141 . W 0 7 S further 9 104 . W 0 7 262 9 346 . W 0 7 352 9 52 . W 0 7 S ALT+F. 9 N 41 . W 0 7 388 9 85 . W 0 7 S you're 9 233 . W 0 7 S on 9 89 . W 0 7 343 9 91 . W 0 7 162 9 93 . W 0 7 S these, 9 273 . W 0 7 174 9 10 . W 0 7 427 9 170 . W 0 7 S find 9 13 . W 0 7 122 9 139 . W 0 7 S convenient 9 141 . W 0 7 12 9 369 . W 0 7 231 9 193 . W 0 7 S ALT+. 9 76 . W 0 7 12 9 149 . W 0 7 243 9 263 . W 0 7 210 9 85 . W 0 7 235 9 154 . 17 59 . P 36 W 0 7 388 9 10 . W 0 7 174 9 20 . W 0 7 S see 9 278 . W 0 7 42 9 16 . W 0 7 293 9 139 . W 0 7 S combination 9 46 . W 0 7 12 9 346 . W 0 7 210 9 52 . W 0 7 212 9 55 . W 0 7 162 9 113 . W 0 7 42 9 315 . W 0 7 301 9 58 . W 0 7 S item, 9 154 . W 0 7 S that 9 89 . W 0 7 S means 9 271 . W 0 7 S it's 9 129 . W 0 7 42 9 N 74 . W 0 7 S shortcut. 9 10 . W 0 7 237 9 215 . W 0 7 210 9 141 . W 0 7 293 9 251 . W 0 7 481 9 254 . W 0 7 S without 9 55 . W 0 7 S opening 9 82 . W 0 7 210 9 154 . W 0 7 301 9 269 . W 0 7 81 9 91 . W 0 7 491 9 93 . W 0 7 260 9 163 . W 0 7 402 9 10 . W 0 7 429 9 136 . W 0 7 S invoked. 9 13 . W 0 7 S They're 9 46 . W 0 7 S configurable. 9 286 . W 0 7 S See 9 111 . W 0 7 S below. 9 263 . 17 59 . P 41 W 0 7 282 9 10 . W 0 7 253 9 170 . W 0 7 S load 9 13 . W 0 7 81 9 139 . W 0 7 S save 9 46 . W 0 7 S files 9 49 . W 0 7 265 9 72 . W 0 7 210 9 76 . W 0 7 407 9 79 . W 0 7 235 9 263 . W 0 7 S A 9 119 . W 0 7 S Document 9 233 . W 0 7 S Set 9 N 60 . W 0 7 39 9 N 64 . W 0 7 42 9 96 . W 0 7 393 9 129 . W 0 7 453 9 497 . W 0 7 S disk. 9 10 . W 0 7 S Each 9 437 . W 0 7 393 9 16 . W 0 7 253 9 141 . W 0 7 S contain 9 251 . W 0 7 S multiple 9 72 . W 0 7 S Documents. 9 311 . W 0 7 S Pay 9 85 . W 0 7 S careful 9 154 . W 0 7 S attention 9 545 . W 0 7 12 9 163 . W 0 7 S whether 9 10 . W 0 7 15 9 13 . W 0 7 39 9 251 . W 0 7 S talking 9 106 . W 0 7 374 9 74 . W 0 7 S Documents 9 311 . W 0 7 277 9 58 . W 0 7 S Document 9 317 . W 0 7 S Sets. 9 123 . W 0 7 S There's 9 93 . W 0 7 S more 9 166 . W 0 7 S information 9 10 . W 0 7 524 9 40 . 17 59 . P 68 W 0 7 S Some 9 10 . W 0 7 S operations 9 136 . W 0 7 S (applying 9 141 . W 0 7 S character 9 145 . W 0 7 S styles, 9 55 . W 0 7 S cut 9 82 . W 0 7 81 9 85 . W 0 7 S paste) 9 154 . W 0 7 S require 9 123 . W 0 7 174 9 96 . W 0 7 12 9 241 . W 0 7 256 9 10 . W 0 7 153 9 278 . W 0 7 S To 9 139 . W 0 7 115 9 141 . W 0 7 S this, 9 69 . W 0 7 S move 9 286 . W 0 7 12 9 193 . W 0 7 343 9 371 . W 0 7 S end 9 311 . W 0 7 162 9 448 . W 0 7 210 9 82 . W 0 7 S area 9 85 . W 0 7 12 9 203 . W 0 7 S mark; 9 269 . W 0 7 288 9 126 . W 0 7 S CTRL+SPACE; 9 10 . W 0 7 619 9 40 . W 0 7 12 9 46 . W 0 7 210 9 251 . W 0 7 S other 9 254 . W 0 7 S end. 9 193 . W 0 7 S The 9 196 . W 0 7 S operation 9 111 . W 0 7 402 9 119 . W 0 7 429 9 319 . W 0 7 S applied 9 321 . W 0 7 12 9 93 . W 0 7 210 9 273 . W 0 7 291 9 10 . W 0 7 7 9 40 . W 0 7 S between 9 46 . W 0 7 210 9 286 . W 0 7 S mark 9 346 . W 0 7 81 9 76 . W 0 7 210 9 79 . W 0 7 S cursor. 9 113 . W 0 7 S Note 9 317 . W 0 7 491 9 87 . W 0 7 210 9 545 . W 0 7 S marks 9 547 . W 0 7 S are 9 163 . W 0 7 S always 9 10 . W 0 7 S placed 9 278 . W 0 7 453 9 101 . W 0 7 210 9 46 . W 0 7 S left 9 104 . W 0 7 S side 9 145 . W 0 7 162 9 74 . W 0 7 210 9 76 . W 0 7 661 9 79 . W 0 7 S Pressing 9 116 . W 0 7 S CTRL+SPACE 9 87 . W 0 7 S again 9 160 . W 0 7 402 9 208 . W 0 7 S remove 9 10 . W 0 7 210 9 278 . W 0 7 S marks. 9 16 . 17 59 . P 7 W 0 7 S In 9 10 . W 0 7 S dialogue 9 20 . W 0 7 S boxes, 9 40 . W 0 7 S CTRL+C 9 69 . W 0 7 402 9 145 . W 0 7 S normally 9 74 . W 0 7 S cancel. 9 149 . 17 59 . P 7 W 0 7 S That's 9 10 . W 0 7 245 9 278 . W 0 7 S Now 9 16 . W 0 7 174 9 43 . W 0 7 253 9 69 . W 0 7 S work 9 106 . W 0 7 S WordGrinder. 9 72 . 17 59 . P 1 W 0 7 S Styles 9 10 . 17 32 . P 2 W 0 7 S Character 9 10 . W 0 7 S styles 9 215 . 17 T 0 19 25 21 S Heading #3 23 25 24 25 26 S H3 28 731 . . P 6 W 0 7 15 9 10 . W 0 7 S supports 9 40 . W 0 7 210 9 104 . W 0 7 S following 9 286 . W 0 7 599 9 196 . W 0 7 S styles: 9 315 . 17 59 . P 2 W 0 7 S Italicised 9 10 . W 0 7 S text 9 16 . 17 T 0 19 25 24 25 21 S List item with bullet 28 S LB S bullet S - S indent 170 26 S LI 23 10 . . P 2 W 0 7 S Underlined 9 10 . W 0 7 S text 9 16 . 17 747 . P 51 W 0 7 711 9 10 . W 0 7 S all. 9 278 . W 0 7 282 9 40 . W 0 7 253 9 141 . W 0 7 231 9 251 . W 0 7 S combinations 9 254 . W 0 7 S if 9 311 . W 0 7 174 9 149 . W 0 7 S like. 9 82 . W 0 7 614 9 317 . W 0 7 S apply 9 203 . W 0 7 S them, 9 123 . W 0 7 256 9 93 . W 0 7 S some 9 208 . W 0 7 7 9 10 . W 0 7 81 9 136 . W 0 7 S then 9 182 . W 0 7 231 9 101 . W 0 7 S Style▷Italic 9 364 . W 0 7 S (CTRL+I) 9 74 . W 0 7 277 9 149 . W 0 7 S Style▷Underline 9 263 . W 0 7 S (CTRL+B) 9 123 . W 0 7 12 9 273 . W 0 7 S apply. 9 241 . W 0 7 614 9 10 . W 0 7 691 9 20 . W 0 7 601 9 215 . W 0 7 231 9 364 . W 0 7 S Style▷Plain 9 49 . W 0 7 S (CTRL+O). 9 76 . W 0 7 S When 9 82 . W 0 7 174 9 119 . W 0 7 202 9 203 . W 0 7 202 9 89 . W 0 7 S new 9 271 . W 0 7 S text, 9 96 . W 0 7 122 9 166 . W 0 7 402 9 10 . W 0 7 S always 9 136 . W 0 7 411 9 40 . W 0 7 S unstyled 9 69 . W 0 7 S (even 9 72 . W 0 7 769 9 76 . W 0 7 S in 9 311 . W 0 7 210 9 149 . W 0 7 S middle 9 82 . W 0 7 162 9 233 . W 0 7 42 9 319 . W 0 7 S styled 9 269 . W 0 7 S word). 9 271 . 17 59 . P 40 W 0 9 10 7 S Tip: . W 0 9 136 7 12 . W 0 9 13 7 202 . W 0 9 139 7 42 . W 0 9 43 7 S new . W 0 9 69 7 45 . W 0 9 254 7 826 . W 0 9 369 7 42 . W 0 9 346 7 S particular . W 0 9 149 7 599 . W 0 9 317 7 S style, . W 0 9 321 7 S rather . W 0 9 547 7 S than . W 0 9 129 7 S changing . W 0 9 10 7 210 . W 0 9 170 7 17 . W 0 9 215 7 162 . W 0 9 139 7 S existing . W 0 9 49 7 814 . W 0 9 72 7 174 . W 0 9 52 7 253 . W 0 9 55 7 115 . W 0 9 111 7 S CTRL+SPACE, . W 0 9 233 7 S followed . W 0 9 545 7 262 . W 0 9 271 7 210 . W 0 9 96 7 7 . W 0 9 208 7 174 . W 0 9 10 7 228 . W 0 9 136 7 12 . W 0 9 13 7 775 . W 0 9 101 7 210 . W 0 9 364 7 17 . W 0 9 254 7 S to, . W 0 9 72 7 870 . W 0 9 311 7 262 . W 0 9 149 7 S CTRL+I . W 0 9 58 7 277 . W 0 7 S CTRL+B. 9 317 . W 0 7 S 9 89 . 17 59 . P 2 W 0 7 S Paragraph 9 10 . W 0 7 728 9 215 . 17 729 . P 48 W 0 7 15 9 10 . W 0 7 735 9 40 . W 0 7 42 9 104 . W 0 7 S number 9 106 . W 0 7 162 9 193 . W 0 7 S paragraph 9 371 . W 0 7 131 9 263 . W 0 7 522 9 233 . W 0 7 S Style▷Paragraph 9 87 . W 0 7 S Styles 9 241 . W 0 7 S (CTRL+P) 9 10 . W 0 7 51 9 182 . W 0 7 210 9 139 . W 0 7 S list. 9 46 . W 0 7 554 9 106 . W 0 7 17 9 72 . W 0 7 S applies 9 76 . W 0 7 12 9 113 . W 0 7 42 9 315 . W 0 7 S complete 9 58 . W 0 7 S paragraph. 9 269 . W 0 7 388 9 96 . W 0 7 174 9 163 . W 0 7 228 9 10 . W 0 7 12 9 136 . W 0 7 477 9 13 . W 0 7 S what 9 40 . W 0 7 17 9 46 . W 0 7 S each 9 106 . W 0 7 903 9 72 . W 0 7 S has 9 79 . W 0 7 S set, 9 113 . W 0 7 231 9 58 . W 0 7 S Style▷Set 9 233 . W 0 7 S Margin 9 91 . W 0 7 S Mode▷Show 9 273 . W 0 7 S Paragraph 9 10 . W 0 7 S Styles. 9 215 . W 0 7 642 9 364 . W 0 7 17 9 49 . W 0 7 S names 9 72 . W 0 7 402 9 76 . W 0 7 429 9 111 . W 0 7 S displayed 9 113 . W 0 7 826 9 154 . W 0 7 210 9 87 . W 0 7 S left-hand 9 123 . W 0 7 S margin. 9 129 . 17 59 . P 14 W 0 7 893 9 10 . W 0 7 728 9 215 . W 0 7 669 9 46 . W 0 7 66 9 104 . W 0 7 S currently 9 286 . W 0 7 S customisable, 9 196 . W 0 7 378 9 119 . W 0 7 S they 9 203 . W 0 7 427 9 89 . W 0 7 S become 9 126 . W 0 7 S so 9 129 . W 0 7 826 9 208 . W 0 7 210 9 10 . W 0 7 S future. 9 170 . 17 59 . P 39 W 0 7 642 9 10 . W 0 7 S RAW 9 170 . W 0 7 903 9 13 . W 0 7 17 9 364 . W 0 7 39 9 254 . W 0 7 S interesting 9 369 . W 0 7 S as 9 111 . W 0 7 122 9 113 . W 0 7 S allows 9 315 . W 0 7 174 9 154 . W 0 7 12 9 269 . W 0 7 S embed 9 123 . W 0 7 S arbitrary 9 93 . W 0 7 S chunks 9 10 . W 0 7 162 9 278 . W 0 7 S data 9 215 . W 0 7 S into 9 43 . W 0 7 165 9 251 . W 0 7 S document 9 286 . W 0 7 S which, 9 76 . W 0 7 S when 9 448 . W 0 7 S exported, 9 116 . W 0 7 669 9 269 . W 0 7 S emitted 9 545 . W 0 7 S intact 9 273 . W 0 7 1013 9 10 . W 0 7 210 9 136 . W 0 7 S output 9 182 . W 0 7 S file. 9 141 . W 0 7 366 9 49 . W 0 7 231 9 254 . W 0 7 S this 9 72 . W 0 7 12 9 371 . W 0 7 S insert 9 55 . W 0 7 S HTML 9 263 . W 0 7 1013 9 85 . W 0 7 2 9 203 . W 0 7 S I'm 9 271 . W 0 7 S writing. 9 96 . 17 59 . P 4 W 0 7 578 9 10 . W 0 7 81 9 215 . W 0 7 581 9 101 . W 0 7 S Sets 9 106 . 17 32 . P 29 W 0 7 554 9 10 . W 0 7 15 9 136 . W 0 7 393 9 46 . W 0 7 253 9 49 . W 0 7 558 9 145 . W 0 7 560 9 76 . W 0 7 S pieces 9 263 . W 0 7 162 9 317 . W 0 7 153 9 203 . W 0 7 540 9 123 . W 0 7 393 9 91 . W 0 7 39 9 160 . W 0 7 S referred 9 129 . W 0 7 12 9 10 . W 0 7 997 9 20 . W 0 7 42 9 437 . W 0 7 542 9 13 . W 0 7 S Set; 9 46 . W 0 7 935 9 49 . W 0 7 S piece 9 369 . W 0 7 162 9 371 . W 0 7 7 9 55 . W 0 7 S within 9 448 . W 0 7 210 9 85 . W 0 7 581 9 154 . W 0 7 S Set 9 91 . W 0 7 39 9 93 . W 0 7 42 9 273 . W 0 7 S Document. 9 10 . 17 59 . P 39 W 0 7 282 9 10 . W 0 7 253 9 170 . W 0 7 383 9 13 . W 0 7 997 9 139 . W 0 7 S many 9 141 . W 0 7 2 9 104 . W 0 7 826 9 74 . W 0 7 42 9 76 . W 0 7 1016 9 55 . W 0 7 S set 9 315 . W 0 7 997 9 119 . W 0 7 174 9 154 . W 0 7 S like 9 269 . W 0 7 S (within 9 91 . W 0 7 S reason). 9 129 . W 0 7 282 9 10 . W 0 7 253 9 170 . W 0 7 S manage 9 13 . W 0 7 S them 9 43 . W 0 7 265 9 251 . W 0 7 210 9 145 . W 0 9 193 7 S File▷Manage . W 0 9 113 7 S Documents... . W 0 9 87 7 301 . W 0 9 545 7 489 . W 0 9 160 7 81 . W 0 9 163 7 253 . W 0 9 10 7 S quickly . W 0 9 13 7 477 . W 0 7 81 9 40 . W 0 7 S switch 9 141 . W 0 7 654 9 106 . W 0 7 2 9 74 . W 0 7 265 9 448 . W 0 7 210 9 58 . W 0 7 578 9 233 . W 0 7 301 9 91 . W 0 7 S (ESC, 9 160 . W 0 7 S D). 9 208 . 17 59 . P 37 W 0 7 578 9 10 . W 0 7 383 9 215 . W 0 7 S their 9 43 . W 0 7 S own 9 104 . W 0 7 814 9 286 . W 0 7 S selection, 9 74 . W 0 7 S viewing 9 113 . W 0 7 851 9 317 . W 0 7 268 9 321 . W 0 7 S position, 9 547 . W 0 7 S etc. 9 10 . W 0 7 642 9 136 . W 0 7 S clipboard, 9 182 . W 0 7 S however, 9 251 . W 0 7 39 9 346 . W 0 7 S shared 9 52 . W 0 7 S across 9 111 . W 0 7 210 9 116 . W 0 7 S entire 9 317 . W 0 7 1016 9 321 . W 0 7 S set. 9 160 . W 0 7 S This 9 241 . W 0 7 1000 9 10 . W 0 7 174 9 278 . W 0 7 12 9 16 . W 0 7 619 9 101 . W 0 7 1063 9 69 . W 0 7 162 9 145 . W 0 7 7 9 346 . W 0 7 S around 9 76 . W 0 7 1083 9 448 . W 0 7 210 9 85 . W 0 7 1016 9 154 . W 0 7 1104 9 91 . W 0 7 265 9 93 . W 0 7 210 9 241 . W 0 7 S clipboard. 9 10 . 17 59 . P 16 W 0 7 S Importing 9 10 . W 0 7 S non-WordGrinder 9 215 . W 0 7 534 9 145 . W 0 7 671 9 52 . W 0 7 S results 9 111 . W 0 7 826 9 58 . W 0 7 42 9 317 . W 0 7 812 9 154 . W 0 7 1016 9 269 . W 0 7 S being 9 93 . W 0 7 S added 9 241 . W 0 7 12 9 10 . W 0 7 210 9 20 . W 0 7 S current 9 278 . W 0 7 1016 9 43 . W 0 7 1178 9 254 . 17 59 . P 2 W 0 9 10 7 S Menu . W 0 9 136 7 S customisation . 17 32 . P 32 W 0 9 10 7 282 . W 0 9 170 7 253 . W 0 9 13 7 S change . W 0 9 43 7 S any . W 0 9 69 7 162 . W 0 9 49 7 210 . W 0 9 145 7 S keyboard . W 0 9 196 7 S shortcuts . W 0 9 315 7 S assigned . W 0 9 319 7 12 . W 0 9 321 7 301 . W 0 9 126 7 S items. . W 0 9 129 7 S These . W 0 9 10 7 669 . W 0 9 170 7 784 . W 0 9 182 7 S saved . W 0 9 43 7 997 . W 0 9 364 7 S part . W 0 9 106 7 162 . W 0 9 145 7 210 . W 0 9 193 7 1016 . W 0 9 111 7 940 . W 0 9 82 7 982 . W 0 9 58 7 935 . W 0 9 154 7 15 . W 0 9 547 7 393 . W 0 9 129 7 S takes . W 0 9 10 7 S its . W 0 9 170 7 1236 . W 0 9 139 7 S configuration . W 0 9 369 7 S with . W 0 9 52 7 245 . 17 59 . P 79 W 0 9 10 7 614 . W 0 9 20 7 115 . W 0 9 437 7 617 . W 0 9 40 7 243 . W 0 9 46 7 210 . W 0 9 104 7 395 . W 0 9 369 7 S navigate . W 0 9 55 7 12 . W 0 9 111 7 210 . W 0 9 263 7 S entry . W 0 9 119 7 174 . W 0 9 203 7 228 . W 0 9 89 7 12 . W 0 9 91 7 S change, . W 0 9 129 7 81 . W 0 9 166 7 784 . W 0 9 10 7 288 . W 0 9 437 7 S CTRL+V . W 0 9 139 7 870 . W 0 9 49 7 262 . W 0 9 286 7 210 . W 0 9 346 7 293 . W 0 9 371 7 174 . W 0 9 311 7 228 . W 0 9 113 7 12 . W 0 9 315 7 S bind. . W 0 9 233 7 282 . W 0 9 87 7 368 . W 0 9 91 7 S bind . W 0 9 160 7 210 . W 0 9 163 7 7 . W 0 9 10 7 1284 . W 0 9 437 7 S keys. . W 0 9 40 7 282 . W 0 9 141 7 285 . W 0 9 104 7 368 . W 0 9 369 7 1307 . W 0 9 52 7 42 . W 0 9 76 7 293 . W 0 9 79 7 S that's . W 0 9 315 7 S already . W 0 9 203 7 826 . W 0 9 269 7 231 . W 0 9 545 7 S elsewhere. . W 0 9 241 7 282 . W 0 9 10 7 253 . W 0 9 170 7 691 . W 0 9 16 7 42 . W 0 9 139 7 S binding . W 0 9 104 7 262 . W 0 9 254 7 S navigating . W 0 9 196 7 12 . W 0 9 79 7 210 . W 0 9 113 7 S appropriate . W 0 9 319 7 301 . W 0 9 123 7 260 . W 0 9 547 7 81 . W 0 9 273 7 352 . W 0 9 10 7 S CTRL+X. . W 0 9 13 7 282 . W 0 9 40 7 383 . W 0 9 46 7 12 . W 0 9 251 7 S manually . W 0 9 346 7 691 . W 0 9 55 7 42 . W 0 9 79 7 1332 . W 0 9 116 7 S from . W 0 9 233 7 42 . W 0 9 203 7 301 . W 0 9 89 7 260 . W 0 9 271 7 355 . W 0 9 163 7 174 . W 0 9 497 7 253 . W 0 9 10 7 1307 . W 0 9 136 7 42 . W 0 9 278 7 S different . W 0 9 46 7 293 . W 0 9 104 7 12 . W 0 9 254 7 245 . 17 59 . P 28 W 0 9 10 7 185 . W 0 9 170 7 15 . W 0 9 141 7 S keybindings . W 0 9 72 7 S (except . W 0 9 55 7 398 . W 0 9 448 7 81 . W 0 9 315 7 301 . W 0 9 317 7 81 . W 0 9 319 7 699 . W 0 9 271 7 S navigation) . W 0 9 10 7 411 . W 0 9 278 7 453 . W 0 9 215 7 210 . W 0 9 101 7 S menus, . W 0 9 104 7 982 . W 0 9 254 7 S they're . W 0 9 52 7 S all . W 0 9 55 7 520 . W 0 9 317 7 S Things . W 0 9 321 7 1108 . W 0 9 126 7 210 . W 0 9 160 7 268 . W 0 9 166 7 192 . W 0 9 10 7 411 . W 0 9 278 7 826 . W 0 9 215 7 210 . W 0 9 101 7 S Navigation . W 0 9 286 7 235 . 17 59 . P 18 W 0 9 10 7 388 . W 0 9 20 7 174 . W 0 9 278 7 S get . W 0 9 16 7 S irrecoverably . W 0 9 286 7 S confused, . W 0 9 196 7 174 . W 0 9 111 7 253 . W 0 9 263 7 S reset . W 0 9 119 7 1392 . W 0 9 203 7 210 . W 0 9 321 7 1373 . W 0 9 129 7 262 . W 0 9 10 7 507 . W 0 9 13 7 42 . W 0 9 215 7 301 . W 0 9 43 7 81 . W 0 9 69 7 220 . W 0 9 286 7 S CTRL+R. . 17 59 . P 78 W 0 9 10 7 S Note: . W 0 9 437 7 S WordGrinder's . W 0 9 251 7 1236 . W 0 9 346 7 S support . W 0 9 311 7 39 . W 0 9 149 7 S at . W 0 9 263 7 210 . W 0 9 58 7 S mercy . W 0 9 203 7 162 . W 0 9 269 7 S which . W 0 9 126 7 192 . W 0 9 96 7 165 . W 0 9 10 7 S platform . W 0 9 182 7 S supports, . W 0 9 69 7 81 . W 0 9 106 7 1037 . W 0 9 72 7 253 . W 0 9 52 7 S vary . W 0 9 311 7 S wildly . W 0 9 82 7 1355 . W 0 9 119 7 1450 . W 0 9 89 7 12 . W 0 9 91 7 S platform. . W 0 9 241 7 218 . W 0 9 10 7 S example, . W 0 9 182 7 453 . W 0 9 40 7 S Linux . W 0 9 364 7 1270 . W 0 9 106 7 S ncurses . W 0 9 74 7 122 . W 0 9 76 7 S doesn't . W 0 9 113 7 S seem . W 0 9 58 7 S possible . W 0 9 269 7 12 . W 0 9 123 7 1307 . W 0 9 547 7 S CTRL+arrow . W 0 9 10 7 1312 . W 0 9 437 7 697 . W 0 9 182 7 S addition, . W 0 9 69 7 780 . W 0 9 254 7 338 . W 0 9 76 7 383 . W 0 9 111 7 192 . W 0 9 82 7 491 . W 0 9 119 7 366 . W 0 9 233 7 381 . W 0 9 321 7 S know . W 0 9 126 7 S about. . W 0 9 10 7 15 . W 0 9 40 7 402 . W 0 9 46 7 1437 . W 0 9 286 7 457 . W 0 9 52 7 378 . W 0 9 55 7 S they'll . W 0 9 82 7 429 . W 0 9 58 7 S labelled . W 0 9 269 7 997 . W 0 9 123 7 S UNKNOWN_1234 . W 0 9 208 7 277 . W 0 9 10 7 S similar. . W 0 9 182 7 388 . W 0 9 40 7 1037 . W 0 9 46 7 S happens . W 0 9 286 7 12 . W 0 9 72 7 S you, . W 0 9 371 7 S please . W 0 9 149 7 1410 . W 0 9 82 7 826 . W 0 7 S touch 9 58 . W 0 7 982 9 203 . W 0 7 366 9 269 . W 0 7 253 9 89 . W 0 7 S make 9 126 . W 0 7 122 9 96 . W 0 7 S display 9 163 . W 0 7 42 9 10 . W 0 7 S sensible 9 33 . W 0 7 S name. 9 16 . 17 59 . P 1 W 0 7 1200 9 10 . 17 32 . P 50 W 0 7 388 9 10 . W 0 7 174 9 20 . W 0 7 381 9 278 . W 0 7 228 9 139 . W 0 7 12 9 364 . W 0 7 S write 9 104 . W 0 7 42 9 369 . W 0 7 1016 9 346 . W 0 7 1355 9 79 . W 0 7 S scratch 9 263 . W 0 7 826 9 233 . W 0 7 S WordGrinder, 9 319 . W 0 7 495 9 96 . W 0 7 1480 9 10 . W 0 7 12 9 182 . W 0 7 S import 9 40 . W 0 7 2 9 69 . W 0 7 826 9 346 . W 0 7 42 9 52 . W 0 7 S small 9 76 . W 0 7 900 9 149 . W 0 7 162 9 58 . W 0 7 638 9 317 . W 0 7 S formats 9 269 . W 0 7 265 9 547 . W 0 7 S File▷Import 9 10 . W 0 7 843 9 40 . W 0 7 S document. 9 141 . W 0 7 1246 9 145 . W 0 7 1392 9 52 . W 0 7 S add 9 55 . W 0 7 42 9 149 . W 0 7 843 9 113 . W 0 7 1016 9 116 . W 0 7 12 9 87 . W 0 7 210 9 89 . W 0 7 862 9 126 . W 0 7 1016 9 10 . W 0 7 940 9 182 . W 0 7 853 9 101 . W 0 7 855 9 104 . W 0 7 S replacing 9 145 . W 0 7 210 9 55 . W 0 7 1218 9 149 . W 0 7 S one. 9 85 . W 0 7 642 9 203 . W 0 7 S followng 9 321 . W 0 7 1574 9 160 . W 0 7 669 9 497 . W 0 7 S available: 9 10 . 17 59 . P 2 W 0 7 S Plain 9 10 . W 0 7 7 9 437 . 17 747 . P 17 W 0 7 S Imports 9 10 . W 0 7 S flat 9 13 . W 0 7 S UTF-8 9 139 . W 0 7 S encoded 9 69 . W 0 7 S plain 9 369 . W 0 7 7 9 371 . W 0 7 1270 9 79 . W 0 7 S no 9 263 . W 0 7 599 9 116 . W 0 7 277 9 269 . W 0 7 903 9 123 . W 0 7 131 9 10 . W 0 7 S Paragraphs 9 13 . W 0 7 669 9 69 . W 0 7 S delimited 9 106 . W 0 7 262 9 371 . W 0 7 S newlines. 9 55 . 17 T 0 19 25 21 S List item without bullet 23 10 26 753 24 25 28 S L 752 170 . . P 1 W 0 7 1042 9 10 . 17 747 . P 42 W 0 7 1614 9 10 . W 0 7 S 'tag 9 13 . W 0 7 S soup' 9 139 . W 0 7 1618 9 69 . W 0 7 1620 9 286 . W 0 7 S HTML. 9 371 . W 0 7 185 9 111 . W 0 7 S unknown 9 263 . W 0 7 S tags 9 233 . W 0 7 669 9 269 . W 0 7 S ignored. 9 545 . W 0 7 S 9 129 . W 0 7 S (or 9 10 . W 0 7 S ) 9 170 . W 0 7 81 9 215 . W 0 7 S 9 101 . W 0 7 669 9 364 . W 0 7 S mapped 9 49 . W 0 7 12 9 346 . W 0 7 S italic 9 52 . W 0 7 81 9 111 . W 0 7 S underline, 9 263 . W 0 7 S respectively; 9 319 . W 0 7 S

    ..

    9 10 . W 0 7 980 9 16 . W 0 7 903 9 364 . W 0 7 728 9 72 . W 0 7 27 9 196 . W 0 7 12 9 79 . W 0 7 S H4; 9 448 . W 0 7 1392 9 315 . W 0 7 S
  • 9 119 . W 0 7 980 9 319 . W 0 7 S LB. 9 91 . W 0 7 1632 9 10 . W 0 7 669 9 16 . W 0 7 1635 9 43 . W 0 7 262 9 286 . W 0 7 S either 9 72 . W 0 7 S

    9 196 . W 0 7 277 9 111 . W 0 7 S
    . 9 113 . 17 1639 . P 1 W 0 7 S Exporting 9 10 . 17 32 . P 31 W 0 7 247 9 10 . W 0 7 451 9 136 . W 0 7 S done 9 40 . W 0 7 1270 9 46 . W 0 7 165 9 49 . W 0 7 S document, 9 369 . W 0 7 174 9 311 . W 0 7 253 9 448 . W 0 7 S export 9 315 . W 0 7 122 9 154 . W 0 7 12 9 87 . W 0 7 42 9 89 . W 0 7 1569 9 545 . W 0 7 378 9 160 . W 0 7 S growing 9 163 . W 0 7 S variety 9 10 . W 0 7 162 9 13 . W 0 7 638 9 16 . W 0 7 1574 9 46 . W 0 7 265 9 286 . W 0 7 210 9 74 . W 0 7 S File▷Export 9 196 . W 0 7 S current 9 58 . W 0 7 S document 9 87 . W 0 7 301 9 547 . W 0 7 S option. 9 129 . W 0 7 642 9 10 . W 0 7 738 9 170 . W 0 7 1574 9 101 . W 0 7 669 9 49 . W 0 7 1607 9 145 . 17 59 . P 2 W 0 7 1610 9 10 . W 0 7 7 9 437 . 17 747 . P 35 W 0 7 1180 9 10 . W 0 7 S produces 9 136 . W 0 7 42 9 101 . W 0 7 S simple, 9 141 . W 0 7 1616 9 254 . W 0 7 1618 9 346 . W 0 7 7 9 196 . W 0 7 1033 9 149 . W 0 7 726 9 116 . W 0 7 81 9 269 . W 0 7 903 9 545 . W 0 7 728 9 10 . W 0 7 669 9 278 . W 0 7 S lost, 9 16 . W 0 7 378 9 46 . W 0 7 1392 9 104 . W 0 7 165 9 286 . W 0 7 7 9 193 . W 0 7 S remains. 9 196 . W 0 7 1632 9 82 . W 0 7 669 9 87 . W 0 7 1635 9 123 . W 0 7 262 9 129 . W 0 7 S newlines, 9 10 . W 0 7 982 9 215 . W 0 7 174 9 139 . W 0 7 623 9 46 . W 0 7 439 9 104 . W 0 7 1270 9 254 . W 0 7 343 9 346 . W 0 7 S very 9 371 . W 0 7 S long 9 79 . W 0 7 S line 9 263 . W 0 7 S per 9 85 . W 0 7 925 9 154 . 17 1639 . P 1 W 0 7 1042 9 10 . 17 747 . P 47 W 0 7 1180 9 10 . W 0 7 S generates 9 136 . W 0 7 42 9 43 . W 0 7 1792 9 46 . W 0 7 S basic 9 49 . W 0 7 1618 9 72 . W 0 7 1620 9 76 . W 0 7 1042 9 113 . W 0 7 1719 9 58 . W 0 7 S nominally 9 321 . W 0 7 1042 9 96 . W 0 7 S 4. 9 10 . W 0 7 S Practically 9 20 . W 0 7 S everything 9 43 . W 0 7 402 9 145 . W 0 7 S read 9 74 . W 0 7 S this. 9 55 . W 0 7 726 9 113 . W 0 7 81 9 154 . W 0 7 903 9 269 . W 0 7 728 9 160 . W 0 7 669 9 10 . W 0 9 170 7 S exported. . W 0 9 101 7 S There . W 0 9 251 7 669 . W 0 9 254 7 780 . W 0 9 346 7 1225 . W 0 9 263 7 S options . W 0 9 233 7 826 . W 0 9 319 7 210 . W 0 9 10 7 S File▷Settings▷HTML . W 0 9 69 7 S export . W 0 9 145 7 S dialogue; . W 0 9 55 7 366 . W 0 9 79 7 231 . W 0 9 113 7 S these . W 0 9 85 7 1020 . W 0 9 203 7 151 . W 0 9 91 7 S program . W 0 9 10 7 S documentation . W 0 9 101 7 12 . W 0 9 46 7 404 . W 0 9 106 7 S underlined . W 0 9 76 7 7 . W 0 9 111 7 411 . W 0 9 116 7 997 . W 0 9 119 7 S . . 17 1639 . P 1 W 0 7 S LaTeX 9 10 . 17 747 . P 60 W 0 7 1180 9 10 . W 0 7 1805 9 136 . W 0 7 42 9 43 . W 0 7 1618 9 46 . W 0 7 1871 9 106 . W 0 7 1016 9 346 . W 0 7 S suitable 9 79 . W 0 7 51 9 58 . W 0 7 S running 9 233 . W 0 7 S through 9 123 . W 0 9 96 7 S XeTeX . W 0 9 10 7 265 . W 0 9 437 7 210 . W 0 9 215 7 S 'xelatex' . W 0 9 251 7 S command. . W 0 9 346 7 282 . W 0 9 371 7 253 . W 0 9 311 7 231 . W 0 9 448 7 S stock . W 0 9 58 7 1871 . W 0 9 203 7 769 . W 0 9 269 7 174 . W 0 9 545 7 S wish, . W 0 9 160 7 378 . W 0 9 163 7 174 . W 0 9 10 7 402 . W 0 9 136 7 S need . W 0 9 215 7 12 . W 0 9 139 7 S edit . W 0 9 364 7 210 . W 0 9 49 7 393 . W 0 9 369 7 12 . W 0 9 193 7 691 . W 0 9 311 7 210 . W 0 9 448 7 S xunicode . W 0 9 317 7 S package . W 0 9 89 7 S --- . W 0 9 126 7 1232 . W 0 9 10 7 S non-ASCII . W 0 9 215 7 S characters . W 0 9 104 7 S probably . W 0 9 193 7 S won't . W 0 9 55 7 718 . W 0 9 448 7 S correctly. . W 0 9 154 7 726 . W 0 9 126 7 81 . W 0 9 10 7 903 . W 0 9 215 7 728 . W 0 9 46 7 669 . W 0 9 104 7 S supported; . W 0 9 52 7 S H1..H4 . W 0 9 111 7 669 . W 0 9 263 7 1673 . W 0 9 317 7 12 . W 0 9 203 7 S \section, . W 0 9 10 7 S \subsection, . W 0 9 139 7 S \subsubsection . W 0 9 72 7 81 . W 0 9 52 7 S \paragraph . W 0 7 S respectively. 9 263 . 17 1639 . P 48 W 0 7 663 9 10 . W 0 7 491 9 136 . W 0 7 441 9 215 . W 0 7 1020 9 43 . W 0 7 265 9 251 . W 0 7 S XeTeX, 9 145 . W 0 7 S TeX's 9 371 . W 0 7 S Unicode 9 111 . W 0 7 1437 9 58 . W 0 7 39 9 87 . W 0 7 972 9 89 . W 0 7 853 9 10 . W 0 7 S poor, 9 278 . W 0 7 81 9 139 . W 0 7 491 9 46 . W 0 7 1446 9 49 . W 0 7 1927 9 72 . W 0 7 669 9 111 . W 0 7 S supported 9 263 . W 0 7 S depends 9 203 . W 0 7 S strongly 9 91 . W 0 7 453 9 163 . W 0 7 932 9 10 . W 0 7 S font 9 136 . W 0 7 451 9 215 . W 0 7 S using: 9 46 . W 0 7 1825 9 254 . W 0 7 210 9 346 . W 0 9 371 7 S Unicode . W 0 7 S section 9 448 . W 0 9 119 7 524 . W 0 9 269 7 S You'll . W 0 9 271 7 1929 . W 0 9 10 7 1908 . W 0 9 136 7 12 . W 0 9 13 7 1911 . W 0 9 139 7 210 . W 0 9 46 7 1031 . W 0 9 254 7 393 . W 0 9 346 7 12 . W 0 9 52 7 1104 . W 0 9 55 7 258 . W 0 9 111 7 1339 . W 0 9 233 7 1990 . W 0 9 269 7 277 . W 0 9 123 7 1104 . W 0 9 271 7 162 . W 0 9 160 7 S fonts. . 17 1639 . P 1 W 0 9 10 7 S Troff . 17 747 . P 44 W 0 9 10 7 1180 . W 0 9 136 7 1805 . W 0 9 43 7 258 . W 0 9 364 7 S ASCII . W 0 9 254 7 2022 . W 0 9 193 7 393 . W 0 9 196 7 1880 . W 0 9 82 7 51 . W 0 9 85 7 231 . W 0 9 154 7 1270 . W 0 9 321 7 210 . W 0 9 91 7 S ms . W 0 9 547 7 S macro . W 0 9 10 7 S package. . W 0 9 182 7 726 . W 0 9 69 7 81 . W 0 9 106 7 903 . W 0 9 371 7 728 . W 0 9 149 7 669 . W 0 9 82 7 1941 . W 0 9 87 7 S exporting . W 0 9 93 7 12 . W 0 9 273 7 S troff . W 0 9 10 7 81 . W 0 9 170 7 784 . W 0 9 182 7 1883 . W 0 9 46 7 210 . W 0 9 104 7 S result . W 0 9 72 7 1885 . W 0 9 55 7 S nroff . W 0 9 113 7 39 . W 0 9 315 7 42 . W 0 9 58 7 S good . W 0 9 154 7 168 . W 0 9 269 7 162 . W 0 9 123 7 S getting . W 0 9 96 7 42 . W 0 9 10 7 S formatted . W 0 9 215 7 1622 . W 0 9 141 7 7 . W 0 9 104 7 S version . W 0 9 346 7 162 . W 0 9 52 7 42 . W 0 9 76 7 S document. . 17 1639 . P 79 W 0 9 10 7 S Non-ASCII . W 0 9 215 7 1927 . W 0 9 104 7 669 . W 0 9 286 7 1620 . W 0 9 371 7 265 . W 0 9 111 7 210 . W 0 9 263 7 S \[char1234] . W 0 9 87 7 S mechanism, . W 0 9 160 7 1446 . W 0 9 10 7 493 . W 0 9 437 7 491 . W 0 9 16 7 769 . W 0 9 101 7 451 . W 0 9 104 7 S lucky . W 0 9 369 7 S enough . W 0 9 76 7 12 . W 0 9 311 7 383 . W 0 9 113 7 42 . W 0 9 82 7 2076 . W 0 9 154 7 162 . W 0 9 87 7 S groff . W 0 9 91 7 S modern . W 0 9 10 7 2101 . W 0 9 278 7 12 . W 0 9 215 7 S handle . W 0 9 46 7 S proper . W 0 9 254 7 S Unicode, . W 0 9 371 7 122 . W 0 9 55 7 195 . W 0 9 263 7 1392 . W 0 9 58 7 198 . W 0 9 203 7 388 . W 0 9 269 7 174 . W 0 9 545 7 S don't, . W 0 9 96 7 784 . W 0 7 1927 9 10 . W 0 7 826 9 16 . W 0 7 210 9 101 . W 0 7 S ISO-8859-1 9 364 . W 0 7 S range 9 346 . W 0 7 402 9 196 . W 0 7 1929 9 149 . W 0 7 S work, 9 119 . W 0 7 81 9 87 . W 0 7 1392 9 123 . W 0 7 S others 9 271 . W 0 7 402 9 10 . W 0 7 429 9 136 . W 0 7 1661 9 13 . W 0 7 S (I 9 46 . W 0 7 381 9 251 . W 0 7 231 9 145 . W 0 7 210 9 193 . W 0 7 S \[u1234] 9 76 . W 0 7 S idiom 9 263 . W 0 7 S because 9 119 . W 0 7 1037 9 321 . W 0 7 S causes 9 126 . W 0 7 2108 9 10 . W 0 7 S 1.18.1 9 437 . W 0 7 12 9 139 . W 0 7 S crash 9 141 . W 0 7 S horribly 9 49 . W 0 7 1020 9 74 . W 0 7 2049 9 55 . W 0 7 1037 9 116 . W 0 7 2080 9 233 . W 0 7 S Blame 9 91 . W 0 7 210 9 96 . W 0 7 2108 9 10 . W 0 7 S team; 9 437 . W 0 7 S they've 9 40 . W 0 7 S been 9 251 . W 0 7 S promising 9 286 . W 0 7 S real 9 196 . W 0 7 1618 9 149 . W 0 7 51 9 116 . W 0 7 S years 9 317 . W 0 9 269 7 S now.) . 17 1639 . P 1 W 0 9 10 7 S Platforms . 17 32 . P 7 W 0 9 10 7 15 . W 0 9 40 7 253 . W 0 9 141 7 429 . W 0 9 69 7 S used . W 0 9 254 7 453 . W 0 9 369 7 S several . W 0 9 196 7 S platforms. . 17 59 . P 2 W 0 9 10 7 S Unix . W 0 9 136 7 S console . 17 729 . P 21 W 0 9 10 7 15 . W 0 9 40 7 402 . W 0 9 46 7 S run . W 0 9 104 7 826 . W 0 9 254 7 42 . W 0 9 145 7 S terminal . W 0 9 196 7 453 . W 0 9 79 7 S practically . W 0 9 317 7 1232 . W 0 9 319 7 2208 . W 0 9 123 7 1450 . W 0 9 273 7 1270 . W 0 9 166 7 42 . W 0 9 10 7 2110 . W 0 9 278 7 S curses . W 0 9 101 7 S library; . W 0 9 106 7 366 . W 0 9 286 7 231 . W 0 9 346 7 1470 . W 0 9 196 7 1270 . W 0 9 149 7 S ncurses. . 17 59 . P 11 W 0 9 10 7 S Underlined . W 0 9 16 7 7 . W 0 9 141 7 39 . W 0 9 69 7 959 . W 0 9 346 7 S underlined, . W 0 9 448 7 81 . W 0 9 315 7 S italicised . W 0 9 269 7 7 . W 0 9 91 7 39 . W 0 9 547 7 959 . W 0 9 10 7 S highlighted. . 17 59 . P 35 W 0 9 10 7 S ncurses' . W 0 9 182 7 1236 . W 0 9 364 7 1437 . W 0 9 145 7 39 . W 0 9 346 7 S quite . W 0 9 196 7 S poor . W 0 9 149 7 81 . W 0 9 82 7 997 . W 0 9 58 7 42 . W 0 9 119 7 2058 . W 0 9 269 7 780 . W 0 9 91 7 192 . W 0 9 160 7 1931 . W 0 9 208 7 2138 . W 0 9 10 7 S such . W 0 9 136 7 997 . W 0 9 13 7 S SHIFT+UP/DOWN, . W 0 9 106 7 S CTRL+arrows, . W 0 9 55 7 S SHIFT+function . W 0 9 233 7 S keys, . W 0 9 321 7 1162 . W 0 9 126 7 697 . W 0 9 93 7 1488 . W 0 9 10 7 1473 . W 0 9 13 7 S enforces . W 0 9 46 7 42 . W 0 9 69 7 343 . W 0 9 106 7 345 . W 0 9 193 7 348 . W 0 9 55 7 1020 . W 0 9 448 7 S responding . W 0 9 154 7 12 . W 0 9 87 7 210 . W 0 9 123 7 S ESCAPE . W 0 9 160 7 S key. . 17 59 . P 19 W 0 9 10 7 282 . W 0 9 170 7 402 . W 0 9 182 7 285 . W 0 9 101 7 1908 . W 0 9 69 7 42 . W 0 9 104 7 S decent . W 0 9 72 7 2219 . W 0 9 311 7 S emulator . W 0 9 116 7 1270 . W 0 9 233 7 1969 . W 0 9 123 7 S support. . W 0 9 273 7 366 . W 0 9 163 7 231 . W 0 9 10 7 S gnome-terminal . W 0 9 43 7 1270 . W 0 9 251 7 210 . W 0 9 254 7 S DejaVu . W 0 9 74 7 S Sans . W 0 9 55 7 S font. . 17 59 . P 1 W 0 9 10 7 S Windows . 17 729 . P 46 W 0 9 10 7 15 . W 0 9 40 7 402 . W 0 9 46 7 285 . W 0 9 49 7 2215 . W 0 7 997 9 145 . W 0 7 42 9 346 . W 0 7 S Window 9 74 . W 0 7 S application. 9 79 . W 0 7 593 9 233 . W 0 7 301 9 269 . W 0 7 1841 9 91 . W 0 7 383 9 129 . W 0 7 2181 9 10 . W 0 7 1214 9 136 . W 0 7 12 9 16 . W 0 7 210 9 101 . W 0 7 S titlebar 9 364 . W 0 7 301 9 369 . W 0 7 12 9 52 . W 0 7 S allow 9 196 . W 0 7 174 9 448 . W 0 7 12 9 315 . W 0 7 1230 9 85 . W 0 7 210 9 87 . W 0 7 1990 9 123 . W 0 7 81 9 547 . W 0 7 S toggle 9 273 . W 0 7 S full-screen 9 10 . W 0 7 S mode 9 40 . W 0 7 453 9 46 . W 0 7 81 9 251 . W 0 7 S off; 9 254 . W 0 7 1037 9 346 . W 0 7 S last 9 76 . W 0 7 427 9 111 . W 0 7 285 9 263 . W 0 7 429 9 85 . W 0 7 1715 9 233 . W 0 7 1270 9 269 . W 0 7 S ALT+ENTER 9 91 . W 0 7 S (note 9 241 . W 0 7 491 9 10 . W 0 7 1037 9 136 . W 0 7 39 9 215 . W 0 7 66 9 139 . W 0 7 S rebindable). 9 46 . 17 59 . P 11 W 0 9 10 7 2241 . W 0 9 16 7 7 . W 0 9 141 7 39 . W 0 9 69 7 959 . W 0 9 346 7 2246 . W 0 9 448 7 81 . W 0 9 315 7 2249 . W 0 9 269 7 7 . W 0 9 91 7 39 . W 0 9 547 7 959 . W 0 7 2254 9 10 . 17 59 . P 13 W 0 7 642 9 10 . W 0 7 2332 9 170 . W 0 9 40 7 2076 . W 0 9 251 7 S provides . W 0 9 346 7 2065 . W 0 9 76 7 1236 . W 0 9 263 7 1437 . W 0 9 233 7 81 . W 0 9 87 7 422 . W 0 9 545 7 293 . W 0 9 547 7 767 . W 0 9 10 7 669 . W 0 7 S supported. 9 170 . 17 59 . P 49 W 0 7 15 9 10 . W 0 7 402 9 40 . W 0 7 115 9 46 . W 0 7 1265 9 251 . W 0 7 S best 9 254 . W 0 7 12 9 346 . W 0 7 1536 9 52 . W 0 7 1969 9 149 . W 0 7 S characters. 9 85 . W 0 7 388 9 545 . W 0 7 210 9 271 . W 0 7 1990 9 96 . W 0 7 174 9 208 . W 0 7 S choose 9 10 . W 0 7 128 9 278 . W 0 7 66 9 40 . W 0 7 558 9 141 . W 0 7 42 9 254 . W 0 7 848 9 145 . W 0 7 S glyph, 9 311 . W 0 7 15 9 82 . W 0 7 402 9 269 . W 0 7 S attempt 9 91 . W 0 7 12 9 129 . W 0 7 461 9 208 . W 0 7 42 9 10 . W 0 7 S glyph 9 33 . W 0 7 1355 9 13 . W 0 7 210 9 139 . W 0 7 S closest 9 46 . W 0 7 S matching 9 286 . W 0 7 2329 9 76 . W 0 7 S Unfortunately 9 149 . W 0 7 1037 9 319 . W 0 7 S works 9 123 . W 0 7 2262 9 93 . W 0 7 S badly 9 241 . W 0 7 1270 9 10 . W 0 7 S bitmap 9 136 . W 0 7 S fonts, 9 40 . W 0 7 S resulting 9 69 . W 0 7 826 9 346 . W 0 7 2262 9 52 . W 0 7 S ugly 9 79 . W 0 7 S results, 9 263 . W 0 7 378 9 154 . W 0 7 122 9 269 . W 0 7 S does 9 123 . W 0 7 198 9 547 . 17 59 . P 26 W 0 7 366 9 10 . W 0 7 1986 9 33 . W 0 7 S suggest 9 16 . W 0 7 S installing 9 69 . W 0 7 42 9 193 . W 0 7 2310 9 52 . W 0 7 S TrueType 9 111 . W 0 7 1969 9 85 . W 0 7 S monospaced 9 269 . W 0 7 2329 9 96 . W 0 7 282 9 166 . W 0 7 253 9 10 . W 0 7 1410 9 170 . W 0 7 210 9 13 . W 0 7 S Consolas 9 40 . W 0 7 1990 9 104 . W 0 7 S pack 9 145 . W 0 7 1355 9 74 . W 0 7 S Microsoft, 9 55 . W 0 7 378 9 58 . W 0 7 366 9 233 . W 0 7 S recommend 9 203 . W 0 7 265 9 271 . W 0 7 2325 9 129 . W 0 7 2327 9 10 . W 0 7 S Mono: 9 136 . 17 59 . P 1 W 0 7 S http://dejavu.sourceforge.net/wiki/index.php/Download 9 10 . 17 59 . P 2 W 0 7 S Neat 9 10 . W 0 7 S features 9 136 . 17 32 . P 3 W 0 7 S Table 9 10 . W 0 7 162 9 437 . W 0 7 S contents 9 182 . 17 729 . P 36 W 0 7 697 9 10 . W 0 7 42 9 20 . W 0 7 1016 9 136 . W 0 7 S containing 9 101 . W 0 7 S structured 9 286 . W 0 7 S content 9 55 . W 0 7 S (like 9 82 . W 0 7 1037 9 317 . W 0 7 S one), 9 87 . W 0 7 174 9 91 . W 0 7 253 9 93 . W 0 7 231 9 129 . W 0 7 S Edit▷Goto 9 10 . W 0 7 12 9 215 . W 0 7 S bring 9 139 . W 0 7 439 9 69 . W 0 7 42 9 49 . W 0 7 699 9 254 . W 0 7 S showing 9 371 . W 0 7 258 9 448 . W 0 7 S outline 9 82 . W 0 7 162 9 154 . W 0 7 210 9 87 . W 0 7 1016 9 123 . W 0 7 81 9 273 . W 0 7 1446 9 208 . W 0 7 1000 9 10 . W 0 7 174 9 278 . W 0 7 12 9 16 . W 0 7 S jump 9 101 . W 0 7 1130 9 69 . W 0 7 12 9 369 . W 0 7 1232 9 193 . W 0 7 S heading 9 76 . W 0 7 826 9 113 . W 0 7 245 9 315 . 17 59 . P 1 W 0 7 S Autosave 9 10 . 17 729 . P 48 W 0 7 15 9 10 . W 0 7 1476 9 40 . W 0 7 383 9 251 . W 0 7 S undo, 9 286 . W 0 7 378 9 74 . W 0 7 122 9 196 . W 0 7 128 9 79 . W 0 7 383 9 263 . W 0 7 42 9 85 . W 0 7 S feature 9 317 . W 0 7 491 9 89 . W 0 7 402 9 271 . W 0 7 532 9 273 . W 0 7 42 9 166 . W 0 7 S timestamped 9 10 . W 0 7 S snapshot 9 40 . W 0 7 162 9 104 . W 0 7 165 9 254 . W 0 7 1016 9 346 . W 0 7 1104 9 79 . W 0 7 S periodically. 9 113 . W 0 7 1180 9 269 . W 0 7 39 9 91 . W 0 7 S disabled 9 547 . W 0 7 262 9 166 . W 0 7 S default; 9 10 . W 0 7 12 9 182 . W 0 7 S enable 9 40 . W 0 7 617 9 69 . W 0 7 S go 9 286 . W 0 7 12 9 72 . W 0 7 S File▷Settings▷Autosave. 9 74 . W 0 7 282 9 87 . W 0 7 253 9 123 . W 0 7 S specify 9 271 . W 0 7 S how 9 241 . W 0 7 1794 9 10 . W 0 7 654 9 136 . W 0 7 S snapshots 9 139 . W 0 7 81 9 106 . W 0 7 932 9 369 . W 0 7 S filename 9 52 . W 0 7 S pattern 9 448 . W 0 7 174 9 119 . W 0 7 228 9 203 . W 0 7 15 9 89 . W 0 7 12 9 163 . W 0 7 S use. 9 166 . 17 59 . P 63 W 0 7 663 9 10 . W 0 7 491 9 136 . W 0 7 S autosaving 9 215 . W 0 7 402 9 104 . W 0 7 S only 9 145 . W 0 7 S happen 9 74 . W 0 7 769 9 79 . W 0 7 174 9 448 . W 0 7 381 9 315 . W 0 7 1528 9 233 . W 0 7 210 9 321 . W 0 7 1236 9 91 . W 0 7 51 9 163 . W 0 7 42 9 497 . W 0 7 S few 9 10 . W 0 7 S seconds, 9 170 . W 0 7 12 9 139 . W 0 7 S prevent 9 141 . W 0 7 42 9 254 . W 0 7 699 9 145 . W 0 7 S box 9 196 . W 0 7 1355 9 111 . W 0 7 1212 9 82 . W 0 7 S popped 9 317 . W 0 7 439 9 321 . W 0 7 S just 9 545 . W 0 7 997 9 93 . W 0 7 451 9 273 . W 0 7 374 9 10 . W 0 7 12 9 437 . W 0 7 288 9 182 . W 0 7 42 9 43 . W 0 7 2302 9 46 . W 0 7 S Autosaving 9 49 . W 0 7 402 9 371 . W 0 7 285 9 79 . W 0 7 2660 9 263 . W 0 7 2662 9 85 . W 0 7 769 9 87 . W 0 7 165 9 89 . W 0 7 1016 9 271 . W 0 7 1104 9 208 . W 0 7 S contains 9 10 . W 0 7 S unsaved 9 182 . W 0 7 S changes. 9 46 . W 0 7 S (In 9 145 . W 0 7 1037 9 193 . W 0 7 S situation, 9 196 . W 0 7 2657 9 116 . W 0 7 128 9 321 . W 0 7 S not 9 126 . W 0 7 S count 9 160 . W 0 7 997 9 208 . W 0 7 S saving 9 10 . W 0 7 165 9 278 . W 0 7 S changes, 9 40 . W 0 7 81 9 104 . W 0 7 165 9 286 . W 0 7 1016 9 193 . W 0 7 1104 9 111 . W 0 7 402 9 263 . W 0 7 S remain 9 85 . W 0 7 S dirty.) 9 87 . 17 59 . P 1 W 0 7 1969 9 10 . 17 729 . P 3 W 0 9 10 7 15 . W 0 9 40 7 735 . W 0 9 104 7 S UNICODE! . 17 59 . P 55 W 0 7 S Mostly, 9 10 . W 0 9 13 7 1440 . W 0 9 16 7 1232 . W 0 9 43 7 S rate. . W 0 9 104 7 S Combining . W 0 9 74 7 1927 . W 0 9 113 7 81 . W 0 9 116 7 S right-to-left . W 0 9 545 7 669 . W 0 9 547 7 S unsupported. . W 0 9 10 7 1180 . W 0 9 136 7 493 . W 0 9 16 7 491 . W 0 9 141 7 174 . W 0 9 251 7 253 . W 0 9 254 7 S use, . W 0 9 346 7 S say, . W 0 9 76 7 S Français, . W 0 9 82 7 S русский . W 0 9 154 7 S язык, . W 0 9 89 7 S 한국어 . W 0 9 93 7 81 . W 0 9 129 7 S 日本語, . W 0 9 10 7 378 . W 0 9 170 7 174 . W 0 9 13 7 368 . W 0 9 101 7 231 . W 0 9 364 7 S Arabic . W 0 9 286 7 277 . W 0 9 72 7 S Hebrew . W 0 9 196 7 2156 . W 0 9 263 7 977 . W 0 9 85 7 623 . W 0 9 154 7 439 . W 0 9 87 7 S mangled . W 0 9 271 7 S (العربية . W 0 9 208 7 81 . W 0 9 10 7 S עברית . W 0 9 437 7 S respectively). . W 0 9 104 7 282 . W 0 9 286 7 253 . W 0 9 346 7 231 . W 0 9 371 7 1796 . W 0 9 79 7 S art . W 0 9 113 7 378 . W 0 9 116 7 1320 . W 0 9 203 7 66 . W 0 9 321 7 932 . W 0 9 126 7 1434 . W 0 7 51 9 10 . W 0 7 81 9 170 . W 0 7 S it'll 9 13 . W 0 7 1929 9 101 . W 0 7 718 9 106 . W 0 9 72 7 S badly. . 17 59 . P 23 W 0 9 10 7 S Unfortunately, . W 0 9 43 7 2636 . W 0 9 69 7 12 . W 0 9 49 7 359 . W 0 9 74 7 S enter . W 0 9 311 7 1969 . W 0 9 315 7 1927 . W 0 9 269 7 39 . W 0 9 123 7 S entirely . W 0 9 273 7 439 . W 0 9 241 7 12 . W 0 9 10 7 165 . W 0 9 136 7 S operating . W 0 9 43 7 S system . W 0 9 49 7 81 . W 0 9 145 7 165 . W 0 9 74 7 2219 . W 0 9 149 7 2343 . W 0 9 203 7 15 . W 0 9 93 7 368 . W 0 9 241 7 S help . W 0 9 10 7 174 . W 0 7 S there. 9 170 . 17 59 . P 22 W 0 7 1180 9 10 . W 0 7 1016 9 136 . W 0 7 1104 9 101 . W 0 7 2707 9 364 . W 0 7 42 9 369 . W 0 7 1969 9 346 . W 0 7 S test 9 311 . W 0 7 1016 9 113 . W 0 7 2542 9 233 . W 0 7 42 9 126 . W 0 7 853 9 547 . W 0 7 587 9 241 . W 0 7 S strenuous 9 10 . W 0 7 S workout 9 215 . W 0 7 855 9 364 . W 0 7 210 9 106 . W 0 7 19 9 369 . W 0 7 S paragraph; 9 371 . W 0 7 477 9 82 . W 0 7 210 9 85 . W 0 7 S Documents 9 154 . W 0 7 235 9 126 . 17 59 . P 1 W 0 7 S Scrapbook 9 10 . 17 729 . P 67 W 0 7 S Edit▷Cut 9 10 . W 0 7 S to 9 182 . W 0 7 S scrapbook, 9 40 . W 0 7 S Edit▷Copy 9 106 . W 0 7 2888 9 371 . W 0 7 S scrapbook 9 55 . W 0 7 81 9 116 . W 0 7 S Edit▷Paste 9 317 . W 0 7 2888 9 91 . W 0 7 2895 9 547 . W 0 7 2357 9 10 . W 0 7 174 9 437 . W 0 7 12 9 215 . W 0 7 1130 9 139 . W 0 7 S copy 9 104 . W 0 7 S fragments 9 145 . W 0 7 162 9 55 . W 0 7 1016 9 111 . W 0 7 12 9 85 . W 0 7 42 9 233 . W 0 7 S scrapbook 9 203 . W 0 7 2080 9 271 . W 0 7 S Fragments 9 10 . W 0 7 1410 9 215 . W 0 7 S appended 9 101 . W 0 7 S onto 9 106 . W 0 7 210 9 72 . W 0 7 623 9 52 . W 0 7 162 9 55 . W 0 7 210 9 111 . W 0 7 2914 9 263 . W 0 7 1270 9 203 . W 0 7 258 9 89 . W 0 7 S optional 9 91 . W 0 7 S timestamp; 9 10 . W 0 7 1037 9 16 . W 0 7 39 9 141 . W 0 7 S configurable 9 69 . W 0 7 826 9 52 . W 0 7 S File▷Settings▷Scrapbook. 9 196 . W 0 7 S (The 9 545 . W 0 7 2914 9 93 . W 0 7 S itself 9 10 . W 0 7 39 9 278 . W 0 7 2688 9 215 . W 0 7 S another 9 43 . W 0 7 S document; 9 106 . W 0 7 174 9 371 . W 0 7 253 9 311 . W 0 7 441 9 448 . W 0 7 2906 9 116 . W 0 7 436 9 233 . W 0 7 12 9 89 . W 0 7 210 9 91 . W 0 7 2914 9 93 . W 0 7 S while 9 10 . W 0 7 1156 9 437 . W 0 7 S it, 9 101 . W 0 7 378 9 364 . W 0 7 122 9 49 . W 0 7 1929 9 286 . W 0 7 1931 9 76 . W 0 7 S behave 9 149 . W 0 7 210 9 58 . W 0 7 168 9 233 . W 0 7 174 9 87 . W 0 9 123 7 S want!) . 17 59 . P 4 W 0 9 10 7 807 . W 0 9 136 7 1395 . W 0 9 40 7 S Go . W 0 9 43 7 S Wrong . 17 32 . P 8 W 0 9 10 7 S Is . W 0 9 20 7 15 . W 0 9 43 7 S behaving . W 0 9 254 7 S funny? . W 0 9 74 7 S Did . W 0 9 196 7 122 . W 0 9 79 7 2688 . W 0 9 263 7 S crash? . 17 59 . P 24 W 0 9 10 7 388 . W 0 9 20 7 S so, . W 0 9 278 7 1524 . W 0 9 101 7 1410 . W 0 9 364 7 826 . W 0 9 104 7 S touch. . W 0 9 72 7 S Software . W 0 9 311 7 S shouldn't . W 0 9 58 7 S crash, . W 0 9 319 7 81 . W 0 9 89 7 769 . W 0 9 91 7 174 . W 0 9 93 7 S tell . W 0 9 163 7 S me . W 0 9 10 7 374 . W 0 9 437 7 2964 . W 0 9 215 7 366 . W 0 9 40 7 402 . W 0 9 46 7 S fix . W 0 9 104 7 122 . W 0 9 254 7 982 . W 0 9 369 7 122 . W 0 9 193 7 S won't. . W 0 9 311 7 S (Probably.) . 17 59 . P 68 W 0 9 10 7 807 . W 0 9 136 7 S reporting . W 0 9 43 7 S bugs, . W 0 9 104 7 122 . W 0 9 254 7 402 . W 0 9 346 7 1533 . W 0 9 76 7 S my . W 0 9 311 7 S life . W 0 9 113 7 S considerably . W 0 9 87 7 S easier . W 0 9 126 7 769 . W 0 9 93 7 174 . W 0 9 129 7 253 . W 0 9 166 7 S (a) . W 0 9 10 7 S duplicate . W 0 9 215 7 210 . W 0 9 101 7 S bug, . W 0 9 69 7 81 . W 0 9 106 7 S (b) . W 0 9 369 7 S show . W 0 9 52 7 3018 . W 0 9 196 7 210 . W 0 9 111 7 S stack . W 0 9 315 7 S trace . W 0 9 233 7 S produced . W 0 9 545 7 262 . W 0 9 271 7 210 . W 0 9 96 7 S debug . W 0 9 10 7 2076 . W 0 9 13 7 162 . W 0 9 16 7 720 . W 0 9 254 7 S (Windows . W 0 9 371 7 S users . W 0 9 111 7 195 . W 0 9 116 7 1410 . W 0 9 317 7 826 . W 0 9 203 7 1528 . W 0 9 123 7 81 . W 0 9 271 7 S I'll . W 0 9 273 7 S send . W 0 9 166 7 174 . W 0 9 10 7 42 . W 0 9 33 7 2906 . W 0 9 278 7 162 . W 0 9 215 7 210 . W 0 9 101 7 S debug . W 0 9 251 7 S version, . W 0 9 346 7 81 . W 0 9 371 7 3063 . W 0 9 79 7 174 . W 0 9 113 7 2636 . W 0 9 116 7 12 . W 0 9 119 7 1410 . W 0 9 203 7 1440 . W 0 9 269 7 210 . W 0 9 545 7 3067 . W 0 9 160 7 S trace.) . W 0 9 10 7 S Without . W 0 9 13 7 457 . W 0 9 43 7 1046 . W 0 9 69 7 S afraid . W 0 9 145 7 S there's . W 0 9 76 7 66 . W 0 9 79 7 42 . W 0 9 149 7 S lot . W 0 9 82 7 366 . W 0 9 116 7 253 . W 0 9 317 7 S do. . 17 59 . S wordcount N 2092 S co 20 S cp 25 S margin 10 S viewmode 25 S cw 33 28 S Welcome to WordGrinder . D 61 P 7 W 0 9 10 7 366 . W 0 9 33 7 381 . W 0 9 13 7 S know, . W 0 9 101 7 366 . W 0 9 141 7 2660 . W 0 9 104 7 718 . W 0 9 145 7 S here! . 17 18 . P 39 W 0 7 S Enclosed 9 10 . W 0 7 669 9 182 . W 0 7 S various 9 139 . W 0 7 S translations 9 104 . W 0 7 162 9 76 . W 0 7 210 9 311 . W 0 7 S phrase 9 448 . W 0 7 S 'I 9 85 . W 0 7 381 9 233 . W 0 7 3141 9 321 . W 0 7 366 9 271 . W 0 7 2660 9 93 . W 0 7 718 9 163 . W 0 7 S here', 9 10 . W 0 7 826 9 278 . W 0 7 42 9 215 . W 0 7 3123 9 40 . W 0 7 162 9 141 . W 0 7 1365 9 69 . W 0 7 S languages. 9 346 . W 0 7 1246 9 149 . W 0 7 383 9 116 . W 0 7 2181 9 233 . W 0 7 S shamelessly 9 269 . W 0 7 S stolen 9 273 . W 0 7 1355 9 10 . W 0 7 S http://crism.maden.org/dunno.html. 9 136 . W 0 7 663 9 149 . W 0 7 491 9 315 . W 0 7 S I've 9 317 . W 0 7 S removed 9 87 . W 0 7 210 9 271 . W 0 7 2757 9 10 . W 0 7 S languages, 9 101 . W 0 7 997 9 286 . W 0 7 15 9 72 . W 0 7 1476 9 149 . W 0 7 1437 9 85 . W 0 7 S that. 9 269 . 17 59 . P 1 W 0 7 890 9 10 . 17 59 . P 8 W 0 7 S Amharic: 9 10 . W 0 7 S አኔ 9 182 . W 0 7 S አላውቅም። 9 40 . W 0 7 S አዚህ 9 69 . W 0 7 S መሰራት 9 106 . W 0 7 S ብቻ 9 72 . W 0 7 S ካልሆነ። 9 74 . W 0 7 890 9 311 . 17 59 . P 7 W 0 7 S Bulgarian: 9 10 . W 0 7 S Не 9 16 . W 0 7 S знам. 9 101 . W 0 7 S Аз 9 251 . W 0 7 S само 9 106 . W 0 7 S работя 9 72 . W 0 7 S тук. 9 196 . 17 59 . P 7 W 0 7 S Bosnian: 9 10 . W 0 7 S Ne 9 182 . W 0 7 S znam. 9 40 . W 0 7 S Ja 9 364 . W 0 7 S samo 9 104 . W 0 7 S radim 9 145 . W 0 7 S ovdje. 9 52 . 17 59 . P 11 W 0 7 S Welsh: 9 10 . W 0 7 S Dwn 9 278 . W 0 7 S i 9 16 . W 0 7 S ddim. 9 139 . W 0 7 S Dim 9 69 . W 0 7 S ond 9 106 . W 0 7 S gweithio 9 369 . W 0 7 S fan 9 55 . W 0 7 S hyn 9 149 . W 0 7 S dw 9 82 . W 0 7 S i. 9 58 . 17 59 . P 7 W 0 7 S German: 9 10 . W 0 7 S Keine 9 13 . W 0 7 S Ahnung. 9 101 . W 0 7 S Ich 9 49 . W 0 7 S arbeite 9 145 . W 0 7 S hier 9 76 . W 0 7 S nur. 9 111 . 17 59 . P 8 W 0 7 S Viennese: 9 10 . W 0 7 S Waa? 9 215 . W 0 7 366 9 43 . W 0 7 S net. 9 46 . W 0 7 366 9 49 . W 0 7 S oarbeit 9 254 . W 0 7 115 9 52 . W 0 7 3286 9 196 . 17 59 . P 10 W 0 7 S Swiss 9 10 . W 0 7 3274 9 437 . W 0 7 366 9 101 . W 0 7 S weiss 9 141 . W 0 7 S es 9 49 . W 0 7 S nid. 9 286 . W 0 7 366 9 193 . W 0 7 S schaffe 9 52 . W 0 7 S nume 9 149 . W 0 7 S hie. 9 315 . 17 59 . P 9 W 0 7 S Ruhrdeutsch: 9 10 . W 0 7 S Wat 9 139 . W 0 7 S weiß 9 46 . W 0 7 S ich. 9 49 . W 0 7 3280 9 369 . W 0 7 S tu 9 74 . W 0 7 3284 9 76 . W 0 7 S nur 9 111 . W 0 7 S malochen. 9 263 . 17 59 . P 9 W 0 7 S Danish: 9 10 . W 0 7 S Det 9 13 . W 0 7 S ved 9 40 . W 0 7 S jeg 9 141 . W 0 7 S ikke. 9 251 . W 0 7 S Jeg 9 145 . W 0 7 S arbejder 9 193 . W 0 7 S her 9 111 . W 0 7 S bare. 9 263 . 17 59 . P 7 W 0 7 S Greek: 9 10 . W 0 7 S Δεν 9 278 . W 0 7 S ξέρω, 9 16 . W 0 7 S εγώ 9 46 . W 0 7 S απλά 9 104 . W 0 7 S δουλεύω 9 145 . W 0 7 S εδώ. 9 76 . 17 59 . P 8 W 0 7 S English: 9 10 . W 0 7 366 9 182 . W 0 7 381 9 16 . W 0 7 S know. 9 46 . W 0 7 366 9 106 . W 0 7 2660 9 286 . W 0 7 718 9 193 . W 0 7 S here. 9 196 . 17 59 . P 9 W 0 7 S Esperanto: 9 10 . W 0 7 S Mi 9 16 . W 0 7 S ne 9 101 . W 0 7 S scias. 9 46 . W 0 7 3385 9 254 . W 0 7 3332 9 369 . W 0 7 S laboras 9 74 . W 0 7 S ĉi 9 111 . W 0 7 S tie. 9 113 . 17 59 . P 10 W 0 7 S Spanish: 9 10 . W 0 7 S Yo 9 182 . W 0 7 1626 9 40 . W 0 7 S sé. 9 43 . W 0 7 3402 9 69 . W 0 7 S nada 9 49 . W 0 7 S mas 9 369 . W 0 7 S que 9 74 . W 0 7 S trabajo 9 196 . W 0 7 S aquí. 9 263 . 17 59 . P 8 W 0 7 S Colombian 9 10 . W 0 7 3400 9 215 . W 0 7 S No 9 69 . W 0 7 S sé, 9 49 . W 0 7 S yo 9 145 . W 0 7 S sólo 9 346 . W 0 7 3414 9 76 . W 0 7 S acá. 9 113 . 17 59 . P 7 W 0 7 S Finnish: 9 10 . W 0 7 S En 9 182 . W 0 7 S tiedä. 9 40 . W 0 7 S Olen 9 69 . W 0 7 S täällä 9 254 . W 0 7 S vain 9 74 . W 0 7 S töissä. 9 55 . 17 59 . P 11 W 0 7 S French: 9 10 . W 0 7 S Je 9 13 . W 0 7 3387 9 16 . W 0 7 S sais 9 101 . W 0 7 S pas. 9 69 . W 0 7 3451 9 254 . W 0 7 3387 9 369 . W 0 7 S fais 9 193 . W 0 7 3412 9 196 . W 0 7 S travailler 9 111 . W 0 7 S ici. 9 317 . 17 59 . P 11 W 0 7 S Irish 9 10 . W 0 7 S Gaelic: 9 437 . W 0 7 S Níl 9 101 . W 0 7 42 9 364 . W 0 7 S fhios 9 251 . W 0 7 S agam. 9 145 . W 0 7 S Nílimse 9 52 . W 0 7 S ach 9 149 . W 0 7 S ag 9 82 . W 0 7 S obair 9 58 . W 0 7 S anseo. 9 203 . 17 59 . P 8 W 0 7 S Gallegan: 9 10 . W 0 7 S Non 9 215 . W 0 7 S o 9 101 . W 0 7 S sei. 9 141 . W 0 7 S Eu 9 104 . W 0 7 982 9 254 . W 0 7 S traballo 9 369 . W 0 7 S aqui. 9 55 . 17 59 . P 7 W 0 7 S Gothic: 9 10 . W 0 7 S 𐌽𐌹 9 13 . W 0 7 S 𐍅𐌰𐌹𐍄 9 16 . W 0 7 S 𐌹𐌺 9 141 . W 0 7 S 𐌸𐌰𐍄𐌰𐌹𐌽𐌴𐌹 9 69 . W 0 7 S 𐍅𐌰𐌿𐍂𐌺𐌾𐌰 9 72 . W 0 7 S 𐌷𐌴𐍂 9 55 . 17 59 . P 13 W 0 7 S Hindi: 9 10 . W 0 7 S मझ 9 278 . W 0 7 S नही 9 215 . W 0 7 S पता 9 101 . W 0 7 S । 9 364 . W 0 7 S म 9 251 . W 0 7 S तो 9 49 . W 0 7 S सिरफ 9 286 . W 0 7 S यहा 9 193 . W 0 7 S काम 9 76 . W 0 7 S करता 9 79 . W 0 7 S ह 9 263 . W 0 7 3529 9 315 . 17 59 . P 7 W 0 7 S Hungarian: 9 10 . W 0 7 S Nem 9 16 . W 0 7 S tudom. 9 43 . W 0 7 S Én 9 49 . W 0 7 S csak 9 286 . W 0 7 S itt 9 193 . W 0 7 S dolgozom. 9 76 . 17 59 . P 8 W 0 7 S Indonesian: 9 10 . W 0 7 S Saya 9 40 . W 0 7 S tidak 9 46 . W 0 7 S tahu. 9 106 . W 0 7 3564 9 346 . W 0 7 S cuma 9 76 . W 0 7 S bekerja 9 111 . W 0 7 S disini. 9 58 . 17 59 . P 6 W 0 7 S Ilocano: 9 10 . W 0 7 S Diakammo. 9 182 . W 0 7 S Agtrabtrabaho 9 69 . W 0 7 S ak 9 371 . W 0 7 S la 9 55 . W 0 7 S ditoy. 9 111 . 17 59 . P 9 W 0 7 S Icelandic: 9 10 . W 0 7 S Ég 9 16 . W 0 7 S veit 9 101 . W 0 7 S það 9 69 . W 0 7 S ekki, 9 106 . W 0 7 S ég 9 346 . W 0 7 S bara 9 52 . W 0 7 S vinn 9 311 . W 0 7 S hérna. 9 113 . 17 59 . P 8 W 0 7 S Italian: 9 10 . W 0 7 3492 9 182 . W 0 7 S lo 9 139 . W 0 7 S so. 9 141 . W 0 7 S Io 9 251 . W 0 7 S qui 9 106 . W 0 7 S lavoro 9 369 . W 0 7 S soltanto. 9 76 . 17 59 . P 2 W 0 7 S Japanese: 9 10 . W 0 7 S 知りません。私はここで働いているだけです。 9 215 . 17 59 . P 10 W 0 7 S Javanese: 9 10 . W 0 7 S Kula 9 215 . W 0 7 S mboten 9 43 . W 0 7 S ngertos. 9 49 . W 0 7 3633 9 74 . W 0 7 S namung 9 55 . W 0 7 S nyambut 9 263 . W 0 7 S damel 9 233 . W 0 7 S wonten 9 321 . W 0 7 S mriki. 9 547 . 17 59 . P 7 W 0 7 S Korean: 9 10 . W 0 7 S 나는 9 13 . W 0 7 S 몰라요. 9 139 . W 0 7 S 난 9 104 . W 0 7 S 여기서 9 254 . W 0 7 S 일핀 9 74 . W 0 7 S 뿐이에요. 9 55 . 17 59 . P 5 W 0 7 S Latin: 9 10 . W 0 7 S Nescio. 9 278 . W 0 7 S Hic 9 43 . W 0 7 S tantum 9 69 . W 0 7 S laboro. 9 145 . 17 59 . P 9 W 0 7 S Marathi: 9 10 . W 0 7 S मला 9 182 . W 0 7 S नाही 9 139 . W 0 7 S माहित. 9 364 . W 0 7 S मी 9 286 . W 0 7 S फकत 9 72 . W 0 7 S इथ 9 52 . W 0 7 3539 9 196 . W 0 7 S करतो. 9 111 . 17 59 . P 8 W 0 7 S Malay: 9 10 . W 0 7 3564 9 278 . W 0 7 S tak 9 40 . W 0 7 3568 9 141 . W 0 7 3564 9 49 . W 0 7 3571 9 369 . W 0 7 S kerja 9 52 . W 0 7 S sini. 9 79 . 17 59 . P 9 W 0 7 S Norwegian 9 10 . W 0 7 S (Bokmål): 9 215 . W 0 7 3347 9 251 . W 0 7 S vet 9 254 . W 0 7 3345 9 72 . W 0 7 3347 9 76 . W 0 7 S bare 9 79 . W 0 7 S jobber 9 263 . W 0 7 S her. 9 317 . 17 59 . P 8 W 0 7 S Saxon: 9 10 . W 0 7 S Wöß’sch 9 278 . W 0 7 S nüsch. 9 43 . W 0 7 S ’Sch 9 49 . W 0 7 S tu’ 9 369 . W 0 7 3284 9 74 . W 0 7 S bloß 9 55 . W 0 7 S arbeidn. 9 448 . 17 59 . P 11 W 0 7 S Nepali: 9 10 . W 0 7 S मलाई 9 13 . W 0 7 S थाहा 9 139 . W 0 7 S छन 9 364 . W 0 7 3529 9 104 . W 0 7 3531 9 106 . W 0 7 3537 9 286 . W 0 7 3539 9 346 . W 0 7 S मातर 9 371 . W 0 7 S गरछ 9 79 . W 0 7 3529 9 113 . 17 59 . P 8 W 0 7 S Dutch: 9 10 . W 0 7 S Geen 9 278 . W 0 7 S idee. 9 40 . W 0 7 S Ik 9 364 . W 0 7 S werk 9 104 . W 0 7 3284 9 145 . W 0 7 S alleen 9 74 . W 0 7 S maar. 9 79 . 17 59 . P 9 W 0 7 3708 9 10 . W 0 7 S (Nynorsk): 9 215 . W 0 7 S Eg 9 104 . W 0 7 3595 9 254 . W 0 7 S ikkje. 9 346 . W 0 7 3777 9 55 . W 0 7 S berre 9 111 . W 0 7 S arbeider 9 315 . W 0 7 3721 9 319 . 17 59 . P 7 W 0 7 S Polish: 9 10 . W 0 7 S Nie 9 13 . W 0 7 S wiem. 9 40 . W 0 7 3242 9 364 . W 0 7 3329 9 104 . W 0 7 S tylko 9 254 . W 0 7 S pracuję. 9 193 . 17 59 . P 9 W 0 7 S Brazilian 9 10 . W 0 7 S Portuguese: 9 215 . W 0 7 3498 9 49 . W 0 7 S não 9 286 . W 0 7 3496 9 346 . W 0 7 3498 9 76 . W 0 7 S só 9 311 . W 0 7 S trabalho 9 149 . W 0 7 3503 9 119 . 17 59 . P 7 W 0 7 S Romanian: 9 10 . W 0 7 S Nu 9 215 . W 0 7 S ştiu. 9 139 . W 0 7 3498 9 69 . W 0 7 S doar 9 49 . W 0 7 S lucrez 9 369 . W 0 7 S aici. 9 76 . 17 59 . P 7 W 0 7 S Russian: 9 10 . W 0 7 3223 9 182 . W 0 7 S знаю. 9 40 . W 0 7 S Я 9 364 . W 0 7 S здесь 9 251 . W 0 7 S только 9 145 . W 0 7 S работаю. 9 371 . 17 59 . P 9 W 0 7 S Swedish: 9 10 . W 0 7 3339 9 182 . W 0 7 3713 9 139 . W 0 7 S jag 9 46 . W 0 7 S inte. 9 104 . W 0 7 S Jag 9 369 . W 0 7 S arbetar 9 74 . W 0 7 3603 9 111 . W 0 7 S här. 9 82 . 17 59 . P 8 W 0 7 S Albanian: 9 10 . W 0 7 S Nuk 9 215 . W 0 7 S e 9 101 . W 0 7 S di. 9 141 . W 0 7 S Unë 9 251 . W 0 7 S jam 9 254 . W 0 7 S punëtor 9 72 . W 0 7 S këtu. 9 55 . 17 59 . P 7 W 0 7 S Serbian: 9 10 . W 0 7 3223 9 182 . W 0 7 3225 9 40 . W 0 7 S Ја 9 364 . W 0 7 3229 9 104 . W 0 7 S радим 9 145 . W 0 7 S овде. 9 52 . 17 59 . P 9 W 0 7 S Sudanese: 9 10 . W 0 7 S Abdi 9 215 . W 0 7 S teu 9 43 . W 0 7 S ngartos. 9 69 . W 0 7 3892 9 72 . W 0 7 S mung 9 371 . W 0 7 S didamel 9 79 . W 0 7 S di 9 116 . W 0 7 S dieu. 9 119 . 17 59 . P 3 W 0 7 S Thai: 9 10 . W 0 7 S ผมไมร 9 437 . W 0 7 S ผมเพยงแตทำงานทน 9 40 . 17 59 . P 8 W 0 7 S Modern 9 10 . W 0 7 S Filipino: 9 278 . W 0 7 S Ewan 9 46 . W 0 7 S ko. 9 49 . W 0 7 S Empleyado 9 145 . W 0 7 S lang 9 55 . W 0 7 S ako 9 448 . W 0 7 S dito. 9 315 . 17 59 . P 8 W 0 7 S Tagalog: 9 10 . W 0 7 S Di 9 182 . W 0 7 S ko 9 40 . W 0 7 S alam. 9 43 . W 0 7 S Trabahador 9 104 . W 0 7 3925 9 52 . W 0 7 3927 9 311 . W 0 7 3929 9 448 . 17 59 . P 8 W 0 7 S Ukrainian: 9 10 . W 0 7 3836 9 16 . W 0 7 S не 9 139 . W 0 7 3834 9 141 . W 0 7 3836 9 49 . W 0 7 S лише 9 254 . W 0 7 S працюю 9 346 . W 0 7 S тут. 9 55 . 17 59 . P 10 W 0 7 S Vietnamese: 9 10 . W 0 7 S Tôi 9 40 . W 0 7 S không 9 141 . W 0 7 S biết. 9 49 . W 0 7 3962 9 72 . W 0 7 S chỉ 9 52 . W 0 7 S làm 9 55 . W 0 7 S việc 9 149 . W 0 7 S đây 9 315 . W 0 7 S thôi. 9 119 . 17 59 . P 7 W 0 7 S Klingon: 9 10 . W 0 7 S jIH 9 182 . W 0 7 S ta'Sov'be. 9 139 . W 0 7 3982 9 254 . W 0 7 S neH 9 72 . W 0 7 S vum 9 52 . W 0 7 S naDev. 9 55 . 17 59 . P 9 W 0 7 S Aragonese: 9 10 . W 0 7 3422 9 16 . W 0 7 S sé 9 101 . W 0 7 3456 9 46 . W 0 7 3402 9 49 . W 0 7 S aquí 9 286 . W 0 7 S nomás 9 193 . W 0 7 3255 9 55 . W 0 7 S treballo. 9 79 . 17 59 . P 8 W 0 7 S Lombard: 9 10 . W 0 7 S Sô 9 182 . W 0 7 S nò. 9 40 . W 0 7 S Ghe 9 141 . W 0 7 S laôri 9 251 . W 0 7 S dômà 9 145 . W 0 7 S chì 9 74 . W 0 7 S ch’inscì. 9 196 . 17 59 . P 8 W 0 7 S Neapolitan: 9 10 . W 0 7 S Nunn’òssaccio. 9 40 . W 0 7 S Ije 9 369 . W 0 7 S accà 9 74 . W 0 7 S ce 9 55 . W 0 7 S fatico 9 111 . W 0 7 3865 9 116 . W 0 7 S bbasta. 9 85 . 17 59 . P 7 W 0 7 S Sixilian: 9 10 . W 0 7 S Nenti 9 215 . W 0 7 S sacciu. 9 141 . W 0 7 S Cà 9 254 . W 0 7 S ci 9 369 . W 0 7 S travagghiu 9 193 . W 0 7 S sulamenti. 9 448 . 17 59 . P 10 W 0 7 S Swabian: 9 10 . W 0 7 S Des 9 182 . W 0 7 S woiß 9 139 . W 0 7 S doch 9 364 . W 0 7 3255 9 106 . W 0 7 3294 9 286 . W 0 7 366 9 193 . W 0 7 S schaff 9 52 . W 0 7 S dähanna 9 111 . W 0 7 S bloß. 9 58 . 17 59 . P 3 W 0 7 S Chinese 9 10 . W 0 7 S (simplified): 9 13 . W 0 7 S 我不知道。我只在这里工作。 9 49 . 17 59 . P 3 W 0 7 4075 9 10 . W 0 7 S (traditional): 9 13 . W 0 7 S 我不知道。我只在這裡工作。 9 106 . 17 59 . P 1 W 0 7 890 9 10 . 17 59 . 3128 N 502 3133 25 3134 25 28 S Unicode test document 3130 25 3131 25 3132 10 . 4089 3136 3135 4 . 1218 4 728 T 8 59 18 32 729 T 0 19 25 21 S Heading #4 23 25 24 25 26 S H4 28 4093 . T 0 19 10 21 S Indented text 28 S Q 24 10 26 S BLOCKQUOTE 752 170 . 747 1639 27 18 4096 4094 1641 1639 749 747 731 729 4093 4091 61 59 35 32 . S addons T 0 2914 T 0 S timestamp B T 1016 2883 2646 S Item from '%N' at %T: . S htmlexport T 0 S italic_off S
    S underline_off S
    S underline_on 1670 S italic_on 1663 . S autosave T 0 S enabled B F S period 215 2646 S %F.autosave.%T.wg . . S statusbar 4102 S idletime 20 28 S /.pool/@shared/workspace/wordgrinder/README.wg S fileformat 20 S clipboard D 3 P 1 W 0 7 S http://www.hanselman.com/blog/UsingConsolasAsTheWindowsConsoleFont.aspx . 17 59 . P 14 W 0 7 366 . W 0 7 2491 . W 0 7 265 . W 0 7 2325 . W 0 7 2327 . W 0 7 S Mono; . W 0 7 S unlike . W 0 7 S Consolas, . W 0 7 1037 . W 0 7 39 . W 0 7 S freely . W 0 7 S available . W 0 7 1355 . W 0 7 S here: . 17 59 . P 1 W 0 7 890 . 17 59 . 3133 25 3134 25 3130 25 3131 25 3132 10 . 301 M 0 S accelerators T 0 S SU S ^U S DELETE S ZDNC S ZWL S SLEFT S ZNP S SDOWN S ^@ S ZM S EC S ^C S EP S ^V S SS S ^W 4156 4155 4162 4161 S ZPP S SUP S ZWR S SRIGHT S SO S ^O S ZBD S SHOME S SM 890 S DOWN S ZD S EG S ^G S ^R S ER 4170 4169 4160 4159 S ZED S SEND 4172 4171 S ^P S SP 4154 4153 S FQ S ^Q S ZL S LEFT S RIGHT S ZR S ZU S UP S ZH S HOME 4197 4196 4164 4163 S SB S ^I 4191 4190 S FS S ^S S END S ZE 4176 4175 4193 4192 4168 4167 4179 4178 S ^F S EF S ZPGUP S PGUP S PGDN S ZPGDN 4209 4208 S ZDPC S BACKSPACE 890 4177 4187 4186 4185 4184 4195 4194 4189 4188 4205 4204 4201 4200 4211 4210 S ^X S ET 4207 4206 4166 4165 4174 4173 4213 4212 4181 4180 4183 4182 4158 4157 S ^K S EN 4215 4214 4199 4198 4203 4202 . . . wordgrinder-0.5.1.orig/testdocs/README-v0.4.1.wg0000644000000000000000000005073712237276001015654 0ustar WordGrinder dumpfile v2: this is not a text file! xy',uVUkx$mɉ%x8!haلZ8%HB1  #@9]RKb) L2ϟ0߳]gΡE}}whFRY*gFf,mjRe6V 37%3f̈14k1`Z}hy\*qv֌4+3B_I&j-8?$ŁچR<7X%~$$Jmc23Rc0(Ցyz3}zm5,ת133gsG8/~<~"aW_@96crYw؈ఠzmh8̰ӣ^-ғ4y͏SfF=\])H_pއz?2ZGG[l\=ӇLD.GoIk*gUsz+l%'ۻP??gb7k8D*h&/''dO6ψ3dKCfMыl?P]>3Dܬpdou7 Az'z" J|nf9q)4^8c#Jahӓ$sVqzf֟_ִ|Jȁj /X/7z&!_ԛA FJ$@Z3}Ԇqpݹ" CfcX%#\iU!=1\k=Kg/6B}EF+@6/J-z8cY3Z‘z2X5xTێf|BOq/~4l~$,i2#0pZ*%9'GJؾJu)l,W*W5 W @@dj![,$׶?@pP%k\W̨=MЉA,,_r6 {p ~ a2A I4P4Яwz10#^lj] kcx}+^k 1|B@c*8yGmۋ˃2_oS]ۅ[C0{®݃ߗo\du>v ,X/QcI=jud*WƊq9߉'FHZ O\<(l*pNt#Fv9m .pRws䇋=%V#F ԰:MV`־h`bj` Jl׊cU={v6dF1.mܓ\.C# KrQ/uT9A#}bu*z_l*A ԰!n$czO芨8~ 7n :¿՛"K\[َ&Ꝝklekr:><\ʾ+}Oo#~ ?%Jd]#LPO=|>/.Cȩ]v`$Wэ7$&IB: {I$ MzlE%)\vPVNNf Y"ĝڈB +Q5Z΍jhFfKኰwj8[6\X)m({/ Br^A *bZ]c/. .B~Rlx)>lXL6!8O2* `iNJ9Q>< %P n@ o+i'UH:+!WlIf8>^*efLZ2as;ZA,{W]ٵv#|,z)d(-=3p|pKjT+wEE>XFHlIYE(2~2ؐiMR2LRZ ,[Yчv\MTɂfĞ76Hh.>ZXK8C:<,zs%U pjSeS哟բ8g@FA^WD2`"Waک|_ިI"4_҅hFa}{q,%jzV\X|!OؤY KpaJF7*0 -f@K>'}8:4N=jZ٬ !ҝp M'ܴ m~Y,_/!Fڏa̙j"_сK⊊" NQU͌\Qxøl4ʵCi;;$ u W\ ˡ`rQ+ iYC[{zpHoT V^ ֔)Dg㩵_P:D7YwXo["n Ƹh٘ J=nzUaFCINH˭|Uv0Pt[%D#6!W1w@;|\rU=<'! 9P칉LN #C']4ء<;*GIYeax6L>b]8u*ӬG:L3Fu(q g^Iz &H8Nxmʈ(f ^nuF2Xm qN[UN E޸/~No¯.~]/fYn iH%ӱPߐ!j|:/wy3j.3AEa6fpY>BڑetT^*tϛ{y8aK&hzxF=BwMX#(#|HU؅ODט.i/|! [#4\S[qC!hO oODAd$kc;"ᖶ<{@k|x Ak qz$Ϟ(A }vlŮbU/#CkтFqS"y3m" k gxJG̙7Uo\5?G4)K͸ b}ܬ뤏0j ZUt+Rjؤ}DG3$]sCS*%PQNyGp@q)_D ]S#ORFceąĆPϜFuQjvi}]i-D,%bôutGҤWi3\Fu _ be+ql#]\RIBtf3I֐!J:c -8eYFvL)R8-V<+PlUО v#z< fp“DJE ܒ %愇~%T3r>]H`x)6Gэ7v@mS{qչ.Nㄧ/jaVL%P6CHx&tVY sQ]twiVp4˚L-LScs |J0|6@stiBGggviۍn@x>zvAJ@NZQv{Q&rG̣ZdO1,oކ"1_-0Mx`~ NN˗ZN%g8 At\,❃E\`Q 1.%DoєUS lxI}S#/bkBs5.`ѽRZRaGFzezꎄv9QʳW G[qBvCdF]'5P 5gsȕƅE@X#Bȁ X`^S@@HXפA5ELE $h%2f&D!,'iH%qH\9'OK ZRz%Alux]piU߇2I$ge*6)?:_C' s51"&PF ,!{wHX5=ny9J̢qP"58o]elBaZsJYSE]Q̷:,EɓyZpi,ytPjʂt܌T f>YO"1 Rڞ4rC?9r;f峈:.7% JK|r7jAd΂# dorRp{摕.^2C/qq89Hw9R#`y؇HٰN3dPGJq$a)5V!4T"2\nu4kPC% =bʁCFuԚIOh0 Y|QW#Djnjy7gh/H{ J3X3$xGwBC]X:=BJX_ ۭdC%'+&> 'B fMξ0`Nr|N}lܚ A='h></g._FpWEx3z}/d/;;qj`As}Hpi]ڋK 0hţ 8H7څm30SOUH"٫@:Mxmx]q0adùjXɺчpgC <`$k8'lcq| !F.%Zk|>]T ny;aM4U{LXK7]^Dv,C/72CYj&WH.*}r!f.jQ7n#F,1җ!B9HؐЋVQG`6Ʊ{YlNNEuѢFȾu80TDBKT >SA3ӎ܃ߧ~;=ؐ9>d5Ao(P+%&׊:-?R8H F4LrgӠWl9GѺ2ðH0 zU1C"mrEzHdެ\ 9]#Bg -?$SѾ5\PU!W W _b|)Zh@0γzi[LmITnq\q 1f@eD2]]Jm1!@m-! h{9~KXǓuj H) E~Ё'~3)3u!_.&NcCCqAsn.j5ӓ|"_ղ\h%j5ogA6}vVqpqyIX܉0F:e8xE;ДFPg\r*ƦJ|S :􌑃thYeΐtQm^F{ \_g.CC3<\"M]u6?R䚷\+Sh xB\tAn75T&nc[j|'Ch14v0+_W/GPͫh4ۗE|b0N}! rޖ7&/ICC&7FFP-Wui+JAOvu?ؿjQ0뚿c?§+جܐdqNv7\NbT8 "ՒJkp=׵(5;77c$g#bg'YđAHi %zXc0$/$*y f"$ŘJ_CPrǔa0 ЅS..iҺm8\SwPpbt;i^.Qpo_  GSKo'[HNi MPZ=I/2*Z <.R)9بJB_= xbS5&3N bu˹uPR*˖i#i}h0IbWI"eFVZCOW YF&ĥuůмA`tzRI|x,)B8d yhLOK^{f>e셻2[Ϡ{qE-bh$f[W]{^P8wx=1Dt>,7+%|@7*os.>VܯB{4臿D]x\RRg?t]nnZ)Vdb*oɠP] ZEi'ab A_ǷIıv皨YQx sv, DL ̻dkN;*0,lҰlYJ2%laxI$(uQc]!JTE[?:RR>inFFP3 W܁yQhMP5Vi|׫Dreg|jZqBFힱ(֒{"yQ{d\ԪB3JDui&S/~e6d u߃󅽿HQ>/O>ħ#\lXw&.zY▁T_WPL_GDf \ ]<"U!3K>@ֽ<<ﰆ)>e_#@tR#6ql_>+]Ȱkpsҙq1~/KI\q5A}-qA o:45 *c]Ec"ZsS;{3'[yqT5~ԛ0V9seI;*E)< D{eb}´so!W،oR6~HnSդ,骸myF|֯iOj-=?yےųV7?j޺5;8yZ[tX} fAu<ޭJBivlV[RDvKd_NLAHӷ Ibلn8 bG(xW8k*jr{wa5? h;z;u2;O<]2|%8 MtC~LoM#"9j768iHfnw{)̆ȭ1d` #ϱ]D6!["-|=rʞV2(t`-\58i?`hEGp9䔛E!>%`+#~╗/._ Ȝ>=xyh?d ų2.BL2^|CŭnE+D6,tqM qdWۮ;a )ȆtGe,e.s+Ux0{|Ӧ8ex g9M4d-05sB.wANS ֽmKE_e>%\w@ u|Hbqa!UG{Dd_!ƨ BF$qo+~7Nŷu4XaL3ѦH|,*)bkzp:ޡGߘp~5R4vg$1J y43,.x$a~U/oˏa,yfjK+J؃ өݞMֈM9]zp}՞1:UTÄx. n~iVpĦeeQjF)Ԟ⠓UߙEfck6I7TJ -`N:yI,`[N"=mys Эq{)AvJ׶٨O\5(G&^}-1-fOj+xNy<|hlde 暊xEԠd54&[ɸJ}mr&žbg;xuB}*/z)*E]6rp6@-H) a‡fD+Q(&Jܞ}x2<7:/?ucM%`gT1Fsw{8Rh2;}'oay-.2dfk c%ݶmz_j*8H=rEcE_P sڢXEb#L4YCLnPE*'PK% :0a< 3w/Fz>HM(?()*$ $c,^3>rVb[~m7Ggn6L:!ϋpqfp[}gny683vFG:sSgn-7.mOG'dEZf.nUFbԏN%OxSNPpS/#vC~ >}=w[O9,?2[/ݝ|Z:3=(hN;Fy:h Q/O CēuSXd h 2Ke7u,Gl 0gNF2Vf. w\eU!W赞2_:IcAѴK.0H'Eѐ]cRI;+,"i}Z*d&Wu.e.t<9N: 9 3Ρ>yྜrs^*URb~"0GRb7 }zD]cc\n=Bũ&6b,ܠ2iTۥ2BAyzI@sOsX'4{84 ;P@ C8D6̌Ig3ɭon/"r׶{nDJKjr*bdIg[~%Gf^sHpC d.)w )Ha{iֳ]weAݡN>뤺\ (UYoBla5Fc6W.Av|mJ)]\2 Fڕ9{S6'}°>^fBQ/[W]O9 0auw9`2osmXO^VVp{o04+`3UM|<&jT^*Llk 7=3 -F񜎸,ɍL=gyn[L FlH<'%Xy@gw*hF}.#neAo=lزu:~ofE۹!K١Hɔu@g3Vm!DgK]̙9qI,{鬜9dm)ή8c?`g띝:;is ~/]}8M"^⽤g;Qj.*a;JJU\5[C5ai2md2~lwuWTijwnv!q7X鹙m4CSh^liMvT_3(ĪHaek%ϑ|AnKű1@9G( "$ǻz*|+ jY׸}E 4+7 _+ޤ~}mL{OiӇ}1 (z3;SIT3ǭ!&v^8)z#&TA,8ЮX)n?0yi`*4-|`]l-}l[>[v<n`#l-?n[ ݆_ï be+&vC+)5#HD}P ʌp!e8a̐n. He@!8 z0Xo\Mc i(!RlO=I!j%pﯞ VZ}']_snÙ5Dȓ-D$۝Ϯ*JT00ͬKYouj(^ŋP82*Xk!r'5H h~F t%*]7wvLQ|Y1g wmrW ǎ|I)m80;^\x991nүVX6Tì‚kڻ*eLW*M⢒4 tp͓hjjݡ(ES[ _TfYk?n?߮{,t eC8C:Bln0%^'x&=mEN+*+`ݴ$ wé5`2v!xo76y꼺 mpd`G8a\_Bs4h`/NR!8̵k,a6E%M]>RiׅDN汢~Ah 3-+/VmY/,#\y$J tf d[Ǯmv=r:NRqtum2lʦ;r|&\Le±4innc`kȷ.׋)S⢑fva5Lb*W21402\뛍/ǜ;ng[wO#KoJTpVT΃Ͳe?v|m@>GlV/}S*{Z_[,|[A 8)}`8HfzS kt7g}*{؈alj7Mfxwn l\vBHX)R!s4h{`)W>,C*6̬˄45CD'=3c3EA8/yvOL" (RLDY*k=Ҏ~PFƚ]'v.dREUj=a,J]nL|1V0[:S:xݥdɯo:};mI7JnuYѰl2\:=}1rBuw3/.I NPaP췑hi*!q%en&aaUax|Q4ԷB̃{a 9 SlJ3[Ev+fmDXyz Mbt*YŅVxsV:bftƹg{_5;22Z&{ pJ4MP6b4dJn ۊj~L"UTm0IzO a&scn;$!aW="J#WXMibP>=Maf6t?r?x;b"u[qOw>O>/?7O/>Qۼ37rOz/vrV.[,Z2V- =W/<| ˖KBXqe8{ <ԏ3k{iܬ =Wƽkp!%^DZ]]{τk ʾ+x%xWeL?Vj<Jy%~Cb \}eHI4}wx \w4}9/z #ȓW/_*kQ#={  |R{ }exi݋/!ӏv3W&5vo'n-wn݅}muێ[w?g ߝ1\EfqjSeݐ~GsҍBMZ_VG;?SjjŞSs̷ž˙X>]܏#}X3:oΛU>B{]m$JxՔ}?2®l@6 d߷kP>cK.rukư4}Q]k-wtU9e^ O;$ 2"aZmܸq 0.By.Klo BJP=WE& ~oI b$;Vbc$'WwlU`u9}ؽH뷐bnYJAM80@B  i3܆μ?"8'1_W<P>~ΏŒ3^wordgrinder-0.5.1.orig/tools/0000755000000000000000000000000012121336473013032 5ustar wordgrinder-0.5.1.orig/tools/multibin2c.lua0000644000000000000000000000157011523520775015615 0ustar -- © 2008 David Given. -- WordGrinder is licensed under the MIT open source license. See the COPYING -- file in this distribution for the full text. local function write(...) io.stdout:write(...) end local function multibin2c(pattern, ...) local files = {...} local id = 1 write('#include "globals.h"\n') for _, f in ipairs(files) do write("\n/* This is ", f, " */\n") write("static const char file_", id, "[] = {\n") local fp = io.open(f, "rb") local data = fp:read("*a") for i = 1, data:len() do write(data:byte(i), ", ") if ((i % 16) == 0) then write("\n") end end fp:close() write("\n};\n") id = id + 1 end write("const FileDescriptor ", pattern, "[] = {\n") for i = 1, id-1 do local id = "file_"..i write("{ ", id, ", sizeof(", id, "), \"", files[i], "\" },\n") end write("{ NULL, 0 }\n") write("};\n") end multibin2c(...) wordgrinder-0.5.1.orig/wordgrinder.man0000644000000000000000000000344312123626544014724 0ustar .\" Hey, EMACS: -*- nroff -*- .TH WORDGRINDER 1 "@@@DATE@@@" "@@@VERSION@@@" "Terminal-based word processor" .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME wordgrinder \- console-based word processor .SH SYNOPSIS .B wordgrinder .RI [ options ] .RI [ filename ] .SH DESCRIPTION .B wordgrinder is a simple character cell word processor that runs on the console. It is primarily designed for text entry. It's designed to get the hell out of your way and let you write; it does very little, but what it does it does well. It supports Unicode, basic paragraph styles, basic character styles, basic screen markup, a menu interface that means you don't have to remember complex key sequences, HTML import and export, and some other useful features. When running, pressing ESC (or ALT + menu shortcut key) will open the menu. .SH OPTIONS These options follow the usual GNU command line syntax, with long options starting with two dashes (`-'). .TP .B \-h, \-\-help Show summary of options. .TP .BI \--lua\ filename Loads and executes a Lua file before startup. .TP .BI \--convert\ srcfile\ destfile Converts from .I srcfile to .I destfile and then exits. The type of the file is autodetected from the extension of the filename. The source file may have a document name suffixed with a .I : for use when importing or exporting WordGrinder files. .SH AUTHOR .B wordgrinder was written by David Given. .br http://wordgrinder.sourceforge.net