From d08879b03a840cc9f794c9e4eb1bc78c1c69a1e6 Mon Sep 17 00:00:00 2001 From: terrorbyte Date: Wed, 19 Feb 2020 15:23:09 -0600 Subject: [PATCH] Finally publish the project --- .gitignore | 2 + README.md | 48 +++++++-- site/about.md | 8 ++ site/i/logo-small.png | Bin 0 -> 43271 bytes site/index.md | 154 +++++++++++++++++++++++---- stages/00-depends.sh | 10 +- stages/01-http.sh | 12 ++- stages/02-removemd.sh | 2 +- stages/03-minify.sh | 4 +- stagnant.go | 126 ++++++++++++++++++++++ gen.sh => stagnant.sh | 41 ++++---- tmpl/footer.html | 5 + tmpl/header.html | 8 ++ tmpl/meta.html | 10 ++ tmpl/style.css | 238 ++++++++++++++++++++++++++++++++++++++++++ util/file.go | 134 ++++++++++++++++++++++++ 16 files changed, 738 insertions(+), 64 deletions(-) create mode 100644 site/about.md create mode 100644 site/i/logo-small.png create mode 100644 stagnant.go rename gen.sh => stagnant.sh (60%) create mode 100644 tmpl/footer.html create mode 100644 tmpl/header.html create mode 100644 tmpl/meta.html create mode 100644 tmpl/style.css create mode 100644 util/file.go diff --git a/.gitignore b/.gitignore index 8fdb9fe..542e873 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ temp/ build/ util/src/ +util/minify +util/mdtohtml hmdtohtml backup/ test/ diff --git a/README.md b/README.md index b64ac47..9d684e3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ +stagnant +======== +!!! Experimental !!! -A pipeline based static "site" generator focused on allowing flexible composability using built in UNIX tools. Fundamentally, this project is simple and only enforces a simple set of rules, filesystem structures, and a small subset of exposed environment variables. +A pipeline based static "site" generator focused on allowing flexible generation. Fundamentally, this project is simple and only enforces a simple set of rules, filesystem structures, and a small subset of exposed environment variables. Reasoning --------- @@ -9,7 +12,15 @@ I write my entire life in markdown, and things that aren't in markdown get gener Additionally, my CI of choice is Laminar-CI which takes the opinionated stance of "just use job shell scripts". I found this rather freeing from implementation specific DSLs or janky non-reproducible web interface configuration management. -I wanted something that allowed me to write a simple step-by-step sychronous pipeline using POSIX.1-2008 shell scripts and allow me to write the pipeline steps in any language I desired. Use the tools you already have. +I wanted something that allowed me to write a simple step-by-step sychronous pipeline using POSIX.1-2008 shell scripts and allow me to write the pipeline steps in any language I felt like programming in. + +Versions +-------- + +stagnant contains 2 supported versions: + +* `stagnant.sh` - A POSIX.1-2008 shell script that contains all the logic for building a site +* `stagnant.go` - The Go implementation that is used for more programmatic logic, will eventually contain an extention to load Go plugins Example Structure ----------------- @@ -107,11 +118,28 @@ Exposed Variables The following variables are exposed to all running stage executables/scripts and to any generator scripts: -* `_FUNCDIR` -* `_SCRIPTDIR` -* `_BUILDROOT` -* `_UTIL` -* `_PROD` -* `_ID` -* `_BUILD` -* `_DEPLOY` +* `_ORIGIN` - The origin directory that is `pwd` +* `_SITEDIR` - Contains all the site files. In my case I use a bunch of .md files and assets that get built +* `_BUILDROOT` - The `build` directory that contains all the build runs and a symlink (`$_BUILDROOT/latest`) to the last build +* `_BUILDID` - The current running build ID, by default uses UNIX timestamps +* `_BUILDDIR` - The current running build and build ID in absolute path form +* `_STAGEDIR` - Directory containing the stage scripts or executables +* `_SCRIPTDIR` - Directory containing scripts that are used for "out-of-build" site management (for example, `clean.sh` cleans my build dirs) +* `_UTILDIR` - Utilities that are required for building are expected to be built into this directory +* `_PRODDIR` - The current "production" build. The idea is that this can contain the code that is committed to CI and all tests and deployments can be run without pulling in all the `stagnant` repo + +Demo +---- + +The demo that is in the default repository requires two projects to either be in `$PATH` or to be in the `util` directory: + +* https://github.com/gomarkdown/mdtohtml +* https://github.com/tdewolff/minify + +Once those binaries are setup simply: + +``` +go run stagnant.go +``` + +and check `build/latest` diff --git a/site/about.md b/site/about.md new file mode 100644 index 0000000..0191483 --- /dev/null +++ b/site/about.md @@ -0,0 +1,8 @@ +//META:title Stagnant Project - Demo About +//META:description About page +About +===== + +This is the stagnant project demo page, the project can be found here: [https://git.sr.ht/~poptart/stagnant](https://git.sr.ht/~poptart/stagnant). + +Bring your own tools. diff --git a/site/i/logo-small.png b/site/i/logo-small.png new file mode 100644 index 0000000000000000000000000000000000000000..7846cf6572e5aecbd8774b36509aed2635726eef GIT binary patch literal 43271 zcmaf3Q*dQnw>`0Kbj*%zbaG;+W7|$T=;$P!j&0kvZKq?~wsrG8{Li;`t-Y#t?Wa|1 z&M^l<6y+t5;PK%B005GdBvAQlfB9PNu+U#`rSm%3uMOHrMgj=<{O`%`D2n^~0_Py9 z=?nlclK;2B+zW(U0RR$!6!3?N`^uTOoBP~Q$|oP&W~+CD)_o)@Nr+iUWCUi?62%t0 zW1nlfPBq3W$mc0*<2$3- zWQ@XH(bI_rfd3-)985^=R5oeO7ZMYH;bvj>`jQ3fslz;lYMZsr=#JsQ=01`QA`u*p z^ret@Ps4j_Gk|Cf+6)hnIi!WC?0;m|B46M17Lw zUqwk68z&qMXJjPpwrr)Sl{V$92^Z6m$bI9`V`k6;4Lmeyzd_iGi~dvj~df9EpUEvyt$~`Bl@ygmZu<$wNLMn4=1lnGqrA5ZJE9PLDkFJFV#Axr=2y? zp)j_Sv@uG+R$VREK%9#;xv3T%S!URp)6&TEkhbNAKKi!|FGrspRdtxJYwGX~Mn@lI zZw}`Wlm=agMJAsb#61;h$#BX?HPZ_VR`u&|4Grc&vf56unajmq@I*Jzxe`nm0pX?L zW2tTeb{a0p_JF^Ljj901^S6$a_V+os>!PCIG!Dzgn)S0uW#L5fxjN2#2*o3s(x~_N zLFQ|orRB`Hu5AJ1B-l6i+lD9ix4&{Aqgsq92Hizv#)WyXgQ8*9!^`2vkVmvb5a=@j z<}+#P$!*p(-5Je51D`kw9RLU?hs_bh6wBa7Bksm08UUx#ph&MrbTVpMzKYPN2Ja#3 z*~K@MK|~|}qXBCSg7aza8Zxo)CxKMO4tr2qnC{3m>V^bZ1UD}$cdKW2Hf+)YKHZzuy(lzZ?!#_T8qfHw0{1AQ&v*$6Y!iY3+F$2upDWT^g$hkTagf0Q7;u;OcfWYQ# z<4A7kYG#-~o<-(E>>d6QG39a|5=@9dR$2tRf2azX{H)r#^Cst3C|u9&&bqh-IBEQ~ zw5Z&IZ%IeyN-+gUQIVq}Ib)1`18%NUbB@~Zm%XVRpi0ee>bMG$*msW>YM@ak53oZO zf{3?mUs*FLL2X-XQ7OKIascT*LFqZa@peMq6zvE_0X=r&z!}rX-U}9BYd3h6ITj6! zdZ>Spxt0wQULu$o5QD(UCu*<@c*=OhHKzxc?}7#&1^99Em^v~#2*fZOa_2O4M8X14 zC*}McMTKEO&V|N65o}YqfB>s-3gj(tP*0pZK!MYRg5vVmd+gcrTHfcweQK!i+g@3U%OxYf|La=&!SaepHsba{8W5u9@$M#Kb0yn}4x@$;YudCA)r%ubiflWD zG$%guZ%oAY1tatqlA-1iYIYC;xkPltf&nlF-7I3kihxPnMcRX8$1O}_X+H=E3TA*G zSTP|OOgJ#|#~vtwqUnyMe*%q+RzmRm$R{IE0@f06HB(~m=wno*QwMF30T}LrQtU?I zCIaxADiejuCoeI2R&NyzV^5KR$JBs+*`VU|NQy&8C}psosM4zrINOVAR5--!PPX#G zX3njAp-+i)Cl#r0KMTDj%{yj9h5!v2ch%A!1{HRXtmQ4{WbU-b5GZC3o(tAhWw2qG zK7nUfQdR){l+@r2UXYwdrb4NJ9l>Ld!`rs_F`WSmp)bQ z7{$_`xL~p{&2b~DpT(oHR?O>!M#xR0>~w?SL@`B0o*ESB2GhlVV8FGXL$SIS%XwIc zTzWk}+eXhm#epwO5V~y-9Um>98Cm3)Z)e;b8jner0Kn0?ZVp*dg=jY(oz01Vi~7a^ ze7SBLEi&NW40yYlN2*`4_AGls)`@dN_(2OWo-OLyof>2Jw{-eX?z&v?&Nh7+NtgVf zKR#ffbB}PFA!yMlq2#nis}Svuc0`gHbFCh@s`pTSK%W794O-21^P;M1Xtcm4t3+lP z%$@MBduw9N+fFWX!A#)iKZ5>a`@O?I(*(#w*v-_TDK6i{WAbcm3x-BVdG8vm{Wd>z z6(AAdeOpS8neZGWh7$SsZ+a0Zc1GoJB{KiL%wbOduWb;#c) z!nuzi#CyFwG>wh83zkhUl_U%$V^zYW9@I82jo@R;r&K4WhbmcKP*a^Z@lMaWHujE; z{KtzQL3p0a-$;b9C}w9l%@tFWy(4K5TGvT*W$){#w3Yd(x730SR#gQvR&jw1-IH{tnZ+@>qK5(Q#%n5Ax!e-Od3)xTfG1&Yp!^5Z zw%muTq3D~K(VkSj4@k)}Rk&)OV6*setd-7SPAXy(boEkojFiLbJ7FQK7cz+cF zND{zbr>7TOOX&043ekr>YHQP z*mzm5?+%iPZ9{#~;Zc;IBxo^EzfY|)(^{Tm^bI12g(^CfNxtEb=HmzFF-}exiV#wX zgeH-A@`_J#O2;YSRh!=uUwV+Gf46$(%WY}V*&)&{>=S$x0e?V16Tu>kwF?`h?TIAa z>eY)h5GC`u5(fVp%jh_Xsd}d`f2^@-u(N-Nv-cRJ48=&B9yutEN-gJI04^M$xD}GO zedPmwEM7Uwxl0Ca8HN_kD%>9#gKn;4y_Cpw(^uY0xqLuDWjCaiw|lHp zt$+sF6;1Wt831DnH^d-P{~Ytoz)zUb<|&6_$muuH>YzggZq2o(aEeFjn-{tiX@z15=W6=0-Tu2 zMv)fQ*4C_)5})e%hoIAEeW%b(=RPYUS~o&Coz8Mkqx(349N!P0+u`(bl^B_?IUh+^ zCO+q=uc0qysA_5RS6$HUBeHDPxPDa`1Y%Zqq->UC#D`&K)wO%b*kMVFEZV3Ybo8S* z`6^L(_+6xmI#&v<$%K-qm9tSRJhQ-^=O$iFZuM+ zMnrhZRItdwU(1cM&f_$djMd??{`g_v0}y^|BF)DeF=Ib04_YWPeC{lh!Bmm%c2Chp zxEL(=VUVZ-HyR-t^>5Z;X>u$MO1S9(}a{ZagCRJB=fwo4W(*tkJ7h5WXDLBF9v z_jh~wS%b>Mn$V2Cody}<#hh1NEjIl@`o7ZuIx{vJ004miN}*OmqjwNVXOT_;34|M^ zn1X49D`=#cq0Lf@(f&y6p|?SuHs>C?b*UMz`bjiPZwpkP=MhUk=En7QhCzy?2z&VG^4yo-BpW3?>{;h8e6Y3pD6s8>Yyf#Q7-&e`D-0-ud`bY z6g#f+!5QF5G(obVjvzzpXx4v7vL>RKwTNKm$7Bzbpj+IA{Uw?xA~?J@pKoXO>CCLs z0WMa-WHl!KUtFTz(p62ri-Wm?^Tv~}OA@Wxvmw|^d5y*8adP8%0s+C5QfMSfeF$tG zV6dc21KMPCKg$)yO-TFAF_fT>Hunc2F7THK! zuwP4pR^KXZ5_^ncn4=%KqS*~9M}?BKWW6OYs}UZ?>mo%udBMoadIW^(YngMJ zAlkac$q8Vw9E|>PKO~k`3iDNEQMgeXv*XyLHJ#RgBg!v240XzkKhwOXI;l1}em{zQ zlwCe}n38miqL$h(W-a1}mAR!Fm+^=prL+82>W#zE&HOqVIreN^uBEVBl9f}ZTa-n@9CXH3yOrVK)Gl} zKk-!MR)*4i(nLyf?1wOaI05`0io@_QT>8NtAZ+wx%rpd$3lIY0ftaiT0LY+ojTKvj zb;JNhXhX7t8dUNWC+RhCC)I%roYN^TE^ZeWn*#O6?h-29*)R|O&q}f4dUm5~hw(nI zw}{j;u`fMEB7+ADC%rjcDUX2%P7aUGgTKHAClo=>aPt7 zZN*=rn6?bK^R@%e{jHd~H6|%aDV&l2&9H)K!X&X{RYet0pW@v}U&g7T=E$n?3EtmH zYi}3zUcu4G_ssxUkaSM!yTT8hCu=$VLxF3~!jKMhdX9#H+9_J3u$8pwKcGzu1ETS^ z`C0O{Uw#at=V6Ec#V~Ud*%PEv%!aFU4&RZCrj5AuK@P!)D#+okB_}pon-bH^<&10z zWGEjpt}QoNHBCRp-fNeIYqA`V9p zdHE^;gu?g6^i)Ho*@upQIGYRF+Wh$$@;Bjk{vM_5_p99z*KoFDy1OGGjVra8kCe}T z+p^kf*9wd#ApY(COk2PZd4T!^^cG&JNfH=fKtNWlTn`5%hR*zO?4~Vrine)h-~?w5 zD~6u(CLq4crO98fryzcrYmD()R`$taa!9E769ZNW%B1bKG@RY@nBISF021!|zLtng z_ye%KayLYBHF*oJzoOXWK-$jPz=;v5e05Y?3sj{ELD=E>_t~@jv*eQ0XNRla6k%&^ zO*z&<9LmN*d1>?)Z+|U)l9uB4uoOxVCZD)Sc#XRRg{y-vMUCZ>p{xZKmO*Eg#}pV6 zlF-RwJeBd=)5Fv8hz!!ie&pMDXiF^f@hV|#kAuyk?(Twvwyk5-gU@YaLBX0}#a0`R zQ)K&ge`G~X{|B6u(OS*vjAT%D_u*=Vp@ZP>2%xR(h zqZ6%M#UN`^k|BZk2>Al`cD=mmwi!(?-yc6dKQ%!Y{ZO$aybZ^VJH4tu;g$-MRhk?z z75eWt@-TV|^+4}RKfy3sw=YlFvOHx0z>L;?#Ap5{-3k8lS$k1Jlth`Kh9v<1#$toMAd8qA4HKI-;P<>&8=lHAjp!}gD5LTv$l zTM<-E_9?K$u-t^onTxK|pS!`PRO94s1R;r3RPNG*PWz8MM~)NRSGRkhe-D;Z@v>;& zD${WCgl=XHkOti;Nnh(svz2`b!v1w3YAjJOfr^H7<79u~lsbtX4{B1PB} zv8J#Ij5$s`@*H}kI$mvn7UuFnq8+A;V zj_{*?e?Hxj;4qZ~O~|3y_wIuw3rbuSON01Pd6*aZDeytr5nOokVLiTTIBtqu7*>KC z6H%xmkGxUHX?YKM*Jf(^OY#|}n_{|IVbbg4;fyo8WwLZ{?r45OJD+?0{l0~$?TYPO zjeC$DfHZB^BpfA4wAiKNFMKUAVVq_pVE?GBt-~<+`QV1j zRp3%YW+sz+bc?Wc2UfchNKWt(3{lyMVtvEmfZCsjFdv%hTfs{6lbdUNax3eBnD66v zFVT|)+PUvpA)J35y|xqG;&c0GZwss;c=+E{7~hXZ)aAJHO6XhJn%MN%&*ngu4tBH7 z_hhsqTz(GY80YRG=&>1_Mkbxe9;H9g!aU}Gu)h(AXgCBzwes6y*XL* zbY^-qE7aE#pgrBRQnK@E_Bp%{+7igUtEi}$OB&k{6nrCQO6$`cSy`!AD52OCVjnxK z)2)@J*f`}j$3I*shKeC1sYWH=V;d(IB0xUBEU7r3{+a}EWW$JHfkeOC7K1J6=w2hf z+O?bPEXp^OigjKugC&g)rzbX5h0K|wOA8G9jb9wKiY~A2XC;^X*Q@Gn>g6;z=#4Q( zMc#vMQ1lh7fRjm1-zGB?Fn(S%$dKy;_GZVwbOihcmRoTXYQZLz?lr zvya9;Q_I?ZoiEWcj)W+A2h=zsyUV!csO+^cj4{eHQUlmim)tC-j(j7k%K0k}1z}uX zHcQJO-U@tEcYm!g=};O>sm4oio~(Q%k^LrcE@{YuDOW(Nc0(4e@eInXP{@j1f70ve zoH~mTpp8HVbSQ(}kcb|vDdW|I000RI72?j$9!aF%UZG;l9ozWtsi4JjnrjZZ3I%z46Lp97C~GP?G?+|Oi91RSyTXw;s3_+dcX`7CG_8}IiI>q&ksoL|D_($`66 zeU8HOds_*%(D?7Kmh8r&9Aw+o5rZvi_r$pZ9DSfMAKTzQ`612$$lS!G&Ro z?6y-Hs>~~O@@WayQI;}Y4}5uPAg82UW;HonnhC0MaOHR_zU&($m#@D(nLau7{RiX0 ztju>?BQ2Jpr}y(JfBK)p`UV4s!P~&7`{P@VDVHkGw};d*mRen-+UumWyJM<$Y7G#Z z6^B(G`BfTTZ^^yeSn`6|mf$kykE*axF~U{kK%7^$9)hXiV78{eElw1uoV02?l93Db^W;sIIXkKL9F00vDE30@21n$!E8wVq8i99e3NPS zaZ5Zc#ER93RgbOF0Mkg~PKdr|An~buy4DOw|Cva5d zb9bGS7bq_}XUP(>lwN>M@WwwlnNMRP+5NAt=U<#H(c=u2_$Wn2Xl@-s~Q4#N&yNyJgA1fm<=9_P@6p<9pW-K9?w!H=fn& zd+$Ti?dbCo5^ae{{A?!wM3AFV2tKRjg%-){=jRO;<~zg>Cp>Kr%l}4ANV?6d?D2{P8S=80wKZ2YilFg3HjdRwkCYery(@%zeqR>8YTXp_vxuqK1 ztpdi%JyHZ153%KICoD#N+8EsPq3y?oFwJZFX2Jka8ydfz2|O9R^`&+pA(3z+FxGlz zIGb_L$L@!x%$a~qREyV$84HpX+l3K-<_#mfO7QBPo?c#Z9Zczy%E{XFP5N6)!*1cl zQ~sJoG%BjN!JnMygQy9*r0!deNy?LJ@70ey+|#r9IgK-`B8DA%U^q}OJ}YK2L|S1z zXPwmr*)6+xEG*ogX%z>pEsM`%kn~*!*TR@(cUvyTF4c=?rGL=}WbT!X*1wWY?9Lxq zY6|B>u}EVNk4iVV<w;qhyGQ>p&U9Qc%D@EcWU;c#b}x^jc8LC-;ESa~>eb+-6$ zO_wu^*haT-%2x6DFJkeYT-D^(nHuj@yKXhHPuTAi5J8d`f(zlOGpP9vfW$>O-L26- zHzg(Xj@paq0+yU|Z%uV&Q1dy}>^LyGLZQDJ5nZ=?MVotZl3?J|tve{&p7 zXgA$F*FCiANgqcn*U}M!=?)Msu4(wSC}0Qx%?xN-`uv)YfNh~eTt)^276tNKSmE;Lp}Wx{Q@v6H^*kiI*xe(d%efWPuNN<8>?NrI z+vFwaSMsPu)qVDetkRs+w6TH!YL&B}*xm%uP8g=!N$j^vy5C>dg2z;~VKu_+bbV)I z66m(mYb){bg^SDIQdW%TW_1=96Uj4mK*FZ5BZY<$w)rm_>L5FOHGiqGT1Yg)xHJFy z#T#yWx_|XsTgF{&o@Xw^9S^}oX2)7 zhm4AB(u+Os(lqrBZ`$N|TWM|Dml%a#+yGam@e;(LDM&(@G4mYoCDwI9_qql`M~y>= zNxy)-TC3WI=UZLISAy7l%$E$W@2`yU<@B0O4;S*O5}OMa>QRQSwqaUR=d{Cbmlr2%g+No& zG8XHFoTqWADHvf_JlvK;BhBGTZd)%t&%?6k<*>srk(UWV>fho+L@x~k_>Wp<0ZWp{ z1L&E)=7F9)4EO?D=kT9TlyN~jrRi;>O57#YHP`QLyJV2<*#@RJ6-b_g86r}iA_Qy2 zmP3n5*r)$0au%lh5!#?pE?>Hzcj@SQxEntc|0K?@6V$mjzfUriV+T7E37_%sE;QV; z$3g(E{u#&wINwr2p9ve#yASWh=#v2Tk|ZB+Y>R8_bHF5}tG{YhLFJH>a_2om!ZT|9 zu?>fhnEyo&ZGUuBO%&Fnm1Fl@-^uPx6HO+uEuLpH_{z3ZhLM(^db_j=#;N%c!A=VM zhStJI)Pg$40mWeFo-7(D!b-YP_ml7aJ8b~SIHNf+U{`~BJ)#>dTwk}wVJ8FeiT2__)6bbMT0QyO!4&|W6MkJKD*KzLW4#H~1 zNisa+o~nkvLYw$Gds!64kp1XEE#}l4i8c#^(?I`Ke5o&#rVTP%{x%3 z{TL~t;kaY}+SGpQIf3rLIe;pR0Z{2_O{Dn#2yrJ3QT*jrfCHGBqk)Q$ijZQ=Ky=t_ zl$#GT&KEEe6tWoDXwkHM9lHZwuR=v=(@ILrr-7os-maHkGxW;A zwiw_z7?+%0A#()QVsEB?0za#Z%OG@IKL$d-tH?Fnnd}Vg>K|+8$LBddoU9|8a-!O_ zO_NSa_Isv%miaLqdmPH*aIn*pe%yd|kT-Z}K^uhmI+sRfS4nv})R7Txu@)o_W4Hj& zCQ7jULyLLKM?s*&ATuh!aQr-4xgu(YXe+MW`U#YJJC#fWE?C`hs`!3&ILzb zbEmq&0HTos2QqEoAcB)w7%_juYm^5tWA|Z#8_d7~lHtjuAllYRoBy7rkUc0AsU5{w zyj?jn3DyfF*V%X;omft_jMY|Vq^#XMZEz6Y)qPthg>0{wNJCXJgHhBrK?3Nfp)Y^Q zaN%ui_C&!v5A&*o#SNxgCkGfItQG&(bT+V5AK!YnGYx~e53QnJb6L=rp37c-<1)~e z^m@eES8(R=$zipRIrmR3N(5%E2zu1cjyl201(64g--&Y#^4ThB!w1NuMe+>j zpb(Q;0nj8O?jq8ytO-fLbWT+H&g~%VI@bwmqZ1>-Sed(&t0!b^Tu~~l6d`LjK z8h2SU7g+$66f6c>Gc7`_e&T=#g+Uc#wu`U^8m>YpEC_XPkR%wF9L+f!7eqcOE5g%* zOT+o2x107bm>hrze&ZbTucCEU|_btRfS>v?%}a9?!h#* zTy3~a1Yn*#z-9+VQdKcHbRw}x?8P}%IEEe(bH#Pw^`FF2ANm*cTrhBPR_MGe9{v6T zt?4C=c`WhOi<@R9{3MTS<)mhddAUX>s-vy`KG2-N&wK{ z;CB|L*Re1-8ksXSV$?G|D_n?2C{aGoZ*~%XvyRId;j=Of*r-4r8)l_C?xAEkVb z;R_?`!4p^z+QIlRIHRc}7drVanCML)6)<-D=RoaW&Z!zf)k;DNc8p&sRS)SljYp~R z3P1;Q*8DJVlgZ z*K>Wi)>`!O`_rWJ61}(ab`OpzmBmHbK}gYj>}ef2=9NI7SNmCv6}Re$tuNd_@Zl zN%N+4{r>fItcUE@fcK5$fOl`$_6=v9-baU3&c(tsAyT10Bl5cDUcLZvT)DdiY3nY? zL{Dv_C?3Bl$h(~Mqy+i|&Z%~KawX%j`L1Z}yc?>X7x;cJv3_zITAs1}NuhxDF8aEk z64$j2O~of#^G%|04y5D>U584VeSe>4ypvmTkJm^S1nkj2sb_L>LUrQlU*V1(4C!C# zD-w^PR=8l+4$@mbX;|c+0JAgUw>uJNo-Nbsgl|?Xu6xM(o16A1ZDjT(j4%6Y(6>^X zb-aXGI8?8y5SqJMo<2CCf4CM>N8aOw2EQ%8!pUSshh?Xb{6*!`sHh0b<}&LLqu~b; zGWW_N`UNyOB5rgUag5NiEguoqk8h|QSk8n48bsoRJ9(4O+j4$7v~Q zuEpiTDN2=~=w@A9YS&jl=&x-;i#{cylYTD&lLGxS_X&+3_s(qc=Y* zonI1QEy(cB52D>e{Su`#-#pWs?EwNiX~AcL+_BA<=G#vTts%p=+B$Ky>xpB0s|*Yu zM7xEUvs^;h1Xc*@hn=ywr<<$!n(L2vW{*C7_&}L$So?F=Mr1O`mVPq!Fl)x4(zxre1gFPLV%a?Kzgg2u-Cj8cy;Ju&mLQ@MYw==eYq z#Z*bOF)4xDO!uWTw5S!oeEps7ku>06I^}607khsEd<#he(UT7q9}NGL>S3Ie{KB>0 z;e+k6gT`v>eRzzH%ck+>O`$nshoWks98Ps#Pz@#?oIvXs)r7@mn4s^S|HvywIt2QR z4<{|duK#I=G~}jJ8lKP9;*UH-Bv{ngcR|nlhImbW%U^F(OYVo2NgwX?ou&xgtsDTh z$yadIf^-Aloq|Xs`zHbKxwU$@y1RA@6maUp?kQ@?W?(!&R%vo3<+dyiscVv4irNKg zS(Qrv3;|D3NPx$l9^vLHukDK7H^5fs>qFY2_b)F1f|o4GWC_`|)^`5`H93Z8L~bs- zlnAMT9Tk@8sFT0VELtKuq=&fyJ$6{XZa-Mk>Dh4As|2Tg3F8WmkaxIqrBE(TGj=OW zFI3<>0Y)`n)Tw>_+l4y$V2NMzqlhK`l~lsBQkqJ766V_O@&?r{y>%>jKI`rV&grUf zX59OXQTOj=kEu`y^D*4ADj3$w-b*>vf{`zJjJk7v0{8x*@NHE;O``>TX+%vb9m0yJ znSbgq-K}awypCS*Nhs#(LOqVcYRXB+5hl;wso%nDycQb8xROGQ42=pE%r8aK1#h}f zxv-(Nb;Q4pkY#=mf>t9s@f`i2@_@e?m06a0=@_$`ns$d!26G0^VQrLWeWJO=n*MI# zV0l2hugII)ieKwsY+UOB3$e2HqDI1}+CkYoyQD-~3@;~Lc2WI9iY(<20#`x%pdU>0 zdviMZw%VvaTG-zO7-POq>-9fXcUdD*8_Q`$^3tzUl!aI{;u?1b6mC#EQ|t!2Ue4>p z3xSgdzC!>A3J{yOcu!er1JV2MMy{W1P`a-$1{$r*!5a=#~K_K!XRBA zCe@wipRtf%{b5|{4zG)s-|n)j4h7Ok2yt7NG7IIqz#X4A@uJ+Icc&^HX=Rci0t*T` zHUfWygPFmis<9YQ1KgNFdH4cvt2My0rB_`Al_@`2V!aIexOcWdDxz=E{dZI3)l-8c zUID8ww1e6!iO=Az8lR}pG%E(nd>B>OBy5Hn@ zL{sq>-G6|n1T1KwJd25^D-MN*z@0!WD%_qVEY|y1BC?64^{icM!s1%T z4EkknigBODe7Si2}mUOGd8&DaO|JA=LkUq-Z7F!>Dl$M$@4gQ{OKxUpOkZ) zd!=QstC!A^L&d^6yZ&25PSrGS_pWBm{OX&GsF&!YJHDC0?F|K6eLZ zJKJ|8hr4JROqF1fk*4;WawwQU#>Onel_yFz33P4bDvyEOo`_VQ_MlovNnB6exkd#6 zZ~7u%&T1UlSzKbpnzE~Yv3T12;14h*eUzQE*sM$#gPxw6%c%CO#$-105Znd5$kY0t zM)Z^CH}Qm|%t#p{zeQl1<1@Z-h94ov?1jSe7(2dqzt=3~P>1hn`?4PoXQ7gz0VxRQ zkvE9pFFz`ONM!YbDjR8^Kew?9KZL4yU&kb%8SgSb*7;r!=qP8nu*vCLMW=->aDvzU zqLpp&W12}sT4|h6_Ct~-`|%7&ez9M_QU!Qu`CN3-cRa1o`ihQDPEr+SuVn+BV4~3z zoH{n!Rr^2iM|-~x$j0a3IEV-#lfA~;x=E`8MgnG6?a&@IxCr{m=n0<&)|01)M_1yy3c zDhjV8#Dht-2aMyFUcR#YnqU7yM;ozF8|PQTuiU*eCgShXGnK%J!CHR2JRwT!tA*$y z`Tzu}70CdIMxi_kxLJu4x85;tn!$Sra{Z-MRm6vr zz_&NU6B%#H=^5Kk_0874jPk4$0A!A1wdOPu7z!}lNvD3dEIRvjZxAjh2rcjs|S5%F(w_f}$E?Y4O7mm34@L1)N= z%&?)3FOHy1B@}DhITVNwT5<3_*azfX~tE4?GVnjk{yX%p-$GK$~phF^A>QTe1>8&kW{dg7125~s{6^g zh0&B@#$ZfvtyST{`)iUw+=2OnG&KC6z7;e^$7Hhen@-|c?TCSRc%-qv!EM&RlM)i= z(4)&ggH)*SZ=1<}a0fxFmk609A1#&0)id!7C=#ET|1^^bXgQ@6BEN-NFhrs6F`+AM zoQLO{^ea|+;XGzJ7ahw=6&7ho|g^Z_; z3!xEtY927iEi|Kp0BC3}i&L`{xsEHMjN*R5f;?uuhQbg z^bIvD%Vq1qqwb5u(9qXib9OYD)^u0XlZNmi2j-UTWA-6cb@a3Ep=r8PkyONORh}lN zdba$WAH97{;{OT17A_X&s}Maw`unKPMQwtQQ+TelKWnW_e>{BOtqBt4nYACaIoIFk zOZ$vKt)`;v9q+6SceSZ z!vkcIX>}07-swYObk|@klgo$`;)%(Gjc*A#CngvmTBQo+PjV0-QfoLTtbbCh+#zI(LSj$}(JAJSyDk9G zLxGg4wkd;~qnx0!MzU9!L3Fg6kYGLK{_ZH+hu%XOk8Mn$2HZKXTK+3`2r=}`;ysmT zoO7TCC2a%6lQi{!$&{+xa0bk-C@&X{!aO59NBFpTBt*=t)N`uq(N}N&D^u6ORn-_b zQ^j`{S2a%e`CG~RJq4p3oWnrtz@mw);PAEWC)yL{^c1$`q3|fY-2?=x!_=@n^yGrS z1@m)pk0$pjV-mx`ngz>#;uR$5ET)jLV0>5_w?ZKjKQn|?pH%a1Ky2CPzI7g`cuCQJ zFH`0HF$G`yosnaPYVLYEVVk}V=jewQ`4eAw(aJ*?ZB(hItOpndVD$$@u89El zMIDayz2lK)H3Uz21s7Dyr1{vzDXP+g@nG)5#tIJ1FO z&s^ulvvJ$8>3MG1g{h`FRW6KRIZp?Z`l`#~ZUYJy)-pUdJ?P?wvSW!d_68p$${6Mz z4s(i`C^Qon2vEE-r!n*5-`u1ih`BW5T-FGa`o?>?Z@Qb2E@K39ka~5o(NFW}P(t@N zy7nebSPasrA^DW%McorPKruK-bqUJiJbaM8QO9oj>pT2R*~>XP6kutseTwdYFA~z| z#vJyFL&*fC#8x=8}wEi~3WJVaS;gwG|~!-9;D0v_sc zxY)pzZSD=eSuLA8y=FH+rAVdHQQv*)Pjx1YNE&+4zI`M*2iWhXI*S!Jt z6T-t@c%XsM0kY>PLiv@uwS4&lTPQdT*XN~OiPST$K&qp;?)ZL0=mhoeE{LaS3J3vl z2_!{W;FA6)uvd|2v9jV5SErwJH9trH3}5yS6DnG~XIv+5T&a!FD1$4O8wIEgn$(B{ zIejmSOkYn&zHkbn$Ib(X#blNT>S*`PnhOez zJ6yED3%z8c+Kl616u^(C$|LZ(g1DUyPNAftgO7=;)?03Kee}N$=w(eY&w_iN9_R=d z95h2Jnb5Ceu%VSp5-Xo*Xk5jiDgIc$Kju3)Y5h~Jh-;AHC#)rTKP|CJlB|EbZi#|q z4wp>m_ZQUyd{@zY!27s|oE%2qri{Q@c7g=*)RB~qYIKjBN%qH6iLt@?MP|`^EQq(r zlvnUWaCVN%j*7dTb!$L^h8TA})U~6BPNPxkuS0YnvPaEu>uT9}l+V&!+^<440_%~t zzR{Lxg{I~Y#VCNE+=TBgNVBzmj42UDUK%xumSVQ98B%JG$DF$5PH(~T{S{+yuxhYx(HD#S-B?FnFA>4J1?hiT07Q<)Zv@(A;DDpn zKNK{ikjj4*VOLf_9Kg@tgS6pt;JMf$?OK!}OY%H?v`OlIj%ulMQo28B*e`eP-^u>@ zuCp?hcC3NR_VRrG^`p*A&Ipi+E#jZN!R|fyKLD3NXuqYJmb{$H37X42HrmzI#RUzD z2so!SwX#MDjMv%o>{J3FkSfryh7l218eT=Z_SU6c{=IYHC%Qyo-y<@m%i3XaD72e)z^X zrFOZIvN%r#Vt5IGsx259D;RUeo#52tJ|F!B1ChtLy1~*{FQUk*7qU9`W~+Uw;e}$Q za`Z7S>;no(-?oK3LGl`N>Jh4V7&eGTZ zZg^pJ%8EwQ6opr@AbUDVj5o?opZfMeZzlKba~MnjP?p5P>yxvE+IH*!QSl#{0T2)p z_oOef?-zmohBP%bQc6P2KF^Oi>eQ^)_V#ulQRP#M6kN9+2}Cs791_?+oIe%!06;R~ zl(}?YR7mwD5q0y26ZuH(>m6hehB&27_h4+*N+f%E*7r}GXjQpH^$N+{Ev4rYRPB|# z!w=bB_vbiwzbGYt0stz2(WO$#&j2X@Ox4tP(^U&Mc>Q) zV-5`>t)MHn2EpXdik0K9nr5UyC5ewmQ|qQNC^0E9K}b$x9IuYbmESArr(ZSraISg0poi@EtoqZ2FKfZLxu!s#3F7!RFl>WiM@$aQ0jogPYMl{&N37j~RR4 z#-x62v&Sc%(rDcu5-s+b`pn3`T=)L$!GZ`EcJAtBdpkj_yEi$B*$=<2s&nch`ua;T ziEa#d_L1r7o{=}F*tT}I61}-`fzJjRrRio25i`oXk?q?d3+E_c>FNT$zGiTY;Hr5G zl0X0;xCrzMT()fktmzBGfTS^PAD!L;=f1XoqXU#0{oO15U}CO9+Uf&;+LNbVlFZ8n;Dd&%7Fw~nODL+oo>HqCA@!Jn zBI6D}`AQhCzemBVE5$XIq&2V=}x}r!>C><$-0nHeY+M(ejwmuhcHT4 zG+KL^it5&y%HlycnC=Cr%oW@rWq#yYLv2VHWbq$MS-BHHxE#s|BqB%}4kA)hNQ>u| z`Z_DvTa=+J}Exq%)Xp<2n@1d`&+`1sd$mllrAY>xl>4PbNg zpnGiWjh`-4{Mv|6D9o#!gIR| z{>wkKXv0|m^nMtB_Ib{gl4%I30Y)lCNL2*44{&kzDKXktDboas zhMi7?!gJE;U(*XIMx!&8+U{L3W6h-+uDjFs6EC#4!>My6^0c)b4LU~Pbj(~?t;mh3 z694HB5!SMRl-pD?=MOLn-3uTl0i?r8U?IR7#@egCociu~`H4Mi>$>x8zX9N`t}ebT zbGkpG%~```xx$6xO;#*^o(O<^>e&8Ds_7b;wyuh8+typY(Az>^@IZI!fFKCfa-N^U z`RuIM_A%n2Oz`5kiW&ug)nC%o@@91IYyOIwS1SAW=Z=)LQYDpGlm;Z3YH5IgXaIbM ziPEVc7&stN+BLO}(aF-2k5+~6T%^*-X9$`b&$)YkqSJY&2*@o$IyZu3JFL*E%Zc9i zQ;n`pQ4();SQf4R4L$=@uC>$l57<;V{?jQ&ESb(c$!mk8;l00~bD=V&0y zN8HlM7g&yutzCZVoCodJtsq|R|HXJr`{X1S-WbJA&e>w`d4bE#2?m1M#B4~xCK!c_ zK)lPYru_e`MZ_VJ&uN z3Y`xZ?3WJ_VR6m!JiFxEr6KP5@j<;OR~%^R8ppL`HdbrZ(ceNlBitFQwPmfV@Y z(A3Ct7}UEEHf~aIdV`dbIeyoPsmf!=2TL`thoAsn>xV85^;<62E1p0Oa+u4fia$A? zid^$U0QoT~-PmgcERrCpq7Z$(K~{aNcCP>DrAeA68LUer41zd%VCmAXbEOK)4I74?-Q9nFtS$4`4PjVc?wHvzn1B;6BSkesn$(1_ zgbihzj2E2MtH)f_a9$uvXDsXCR5ZuL%H{Ope<1Zh*P-;xbY zCgtqmHWbrS7}HqXQL%f|f?j|9g5Kh~=C1M9nv+A9)OQT8t3Q=(Ya6o>dTVPtJUr|R zjyk|F&SKgUYEACAd42Y?)w6}q;(Z{>VlsybilnWJdVWEF04V|0hUZ$CC~1La+$}I$ z`6ZBO=!S)aBsCTA-wbSM@~eX&91;qx zzHVaX-(T?g^jC$jbC}SNg_J9{sGw??I>QyG|DX_1V9Y-Ph!TZX|FNMg{jx!--hIn` zt@Z8w8+8CcaU&LRQ69z!wOQf$t4ynQiAEaUj}e{EMa37gBDioo0KzdciA3y>7o48Z zG&donyTX^f1GPFnCrf?Z@(H_SAKA*J#F0z{Gb2f?tz}ZujQ*FH(je>>nepVs9Un++2xawU>N{W%jHZ#!I?W!@%c69v1R}O;DZks zFsQ@;Xlgv?nqX1ZDFp}ifE3Lkt94$fa9|Gy?<*jwO|kj=@#KQLB+t#!wdxxwiB8+j z*yj+)KgQVHMM!uR!HK_0>R0|{b9CX=$Hp#ASNw#AgolKz%vEXqdy4kK8IMo=gMw=w z=3Kj9ipWD4tRGNFev6{S56aGgFH6cjRc*SN$?@fGaI82C!?TiH6P}i>c>O!MC>Jpz zchf-0{AMTG_I98&bz8NNPJDFwTPL1);^;flhIz*h5QZAU5_pu!=^jV)OuTv2({HSm zNDNjm(o`BO*Wdy#aJis?p56k8XlepUq$4&SY~OxvrI}ii77{r#Qc4{ovNn(tO3ChU zXFC|)ECRuK+sAH3!1DR$hPap-RcZL{GCe}4r=Hx+MD8mRdNf4U^IFBh< zbWAz9qO4Edh>Z;)Xg&uI`5i5=6%(D`5hgENG;AVh$6Kf{~dL(w8pd49%trd-uU~zGKkm6T@7V%z#bX9FIG1H9ANI6$t1+ zz&!7Bl!p2$Mfd5S20UAB$VGS9Y9}jK9)HXZPTpVfyAa@+djdW^ZdXn{)6+Cq+R$*& z+4apAhs(U{VPA}VMoRuyfM{8ziBFnz(f?6t)s;GJ5){|u6;k&>%ed-u3Qe||X1d08 z2LPiA5>hi={!$`hF8lyNp$|*xRWXq@IM1&{icx*OYBjrOwBY-rKLMn4s!H`8Av506 zE;mvDfcE}IttRpAwTwHFp0R&9@S`8~zN5)1dfs6SmChi9dIQ^c&(|TA@iP*EPc2#< zo$ZQ$5$G3y)P_w;L8%u|WQvxXwN0>7H;<&^>l+o4X#^zl7^56>eD|&WOcfB)fsoL7 zS8d)5Y}`3{)?-620D)VPoobyCr|fd+$WzSEy@-%PF&L_%sb;9>wTi4AJ3uVW>?_Yt zYNt!(6aOrD<0~yf#Os>Mv*K+^kEFf&lQqcr_-h{MzyQI6%MccilWB;P9yr) zRGRu{Dd|;A&g^!B(f=d3coiYJR;O(rOfIgPm1x_%8CbzSB54chcO9={$^eS~E)%7izem^0^%d{B}_0Ut4aIg@B_O63sd|aY6XDC_+#xWx1a-xlahPd)RaAxB7h*)Hl5Kx0HcGIa_*lH->aO$5PF{A$S}e2X@bH? z!O<%v@E#%kf5KorO;Csfz)~)PrHmJNAjW@e2e16uq}}yfRzwcIeKQ z6)9+oluQ|^eB;@iV@%z&p!coOtA+prg|{=bcJpRn&vXo*RY`oes-QoJ8R=dHn$j4B zEXL1%0i$V;kmmPRC+_~xsQ;6%?>rGHH>kdz3eRl_huc z*HB_zC4_p)3(h`T^be1MbeoAt7R#*nIA$~p`M8~ls5q~faf%W)!PYd!R#?F;>rLaD z(aUczrd}C*YCJ1mboPF4(0|=RBLt$OgHMR2nOzD=eo3X7dlYUEKyryE`X8Nf^~(9K z)&CiJvS*&MvZnb8;SM)5F`-WT-}ARgKs#pnA5Q@+gl$RWSNVkt8|W9dk4^+8peY~~ zFE5L7Hm12$I8#P|CMk_o&oS?7G)C&)dK$>~H-F-{r(^lmP|jvYp9D#Iqw&Z`5Fxj6 z;r*Ob_n?oa_ZR%re@AHq=G5GG_kx(y)Kv3FzY_^x@o^-LWsK*tz8HDj_Xhq(2>2Pn z=`ul)ZYGM~Ljb-?tM3fytJY&$V{3H9lvxT5uWsopy4HXWkxN$AM1S{>Qt?YSsHFNQ zWpDhGjyv%n!CFzrskybuTW@Ji{^`xvZ>j&an)TJIk2+=k{0oea@0PN1gGrnJr3x%Q4s_)~uaQVu#n*F%nT_+*ZI74MApI`Kto7Ay=^ z$L_cbLug(lc zH~@RuD0~~j;nm%-=IdG_O4DsE4WC`yZ~W~yckb+;-ntbyLLg6^fLtZ@%43>Zvr$T~ zC2v3d_j!RF_%9fXl${DtrV%J0Z$=7ga9)BT)t~j-QXE1AtC#>XHRYE^O#;g0cSiQ? z*;>AkrmtSu8AIhV1VI5juXtV{j*W4o(R589^L+#5Z@jv9@7pApQ~)U*va1x5dN1Qs{rTbp zyJaxrN412iR3{YxfT<~veSJs0?(D9fsq#ZV1i+D&%x6BBUeowSvQXEOF3v=845Y{* zq-891-qNrj+JD7%CDV$;BqRY0z}RqX_oZXyW4-4ghb6WlQVZx(WHd4@{I0 z=jFHcJlnPbmVpv8)wS0UlpfWzcr25cdxu7hcf4!$$_w3Q~R~}(9TMnyBuA=}hmoV)5gU13f z3?XIN?}79x5-K7Qy5~<*v3LD_Sh@Odt5es0JUq94@$xI~YFoPL#<|c7FnPLfB46J7 zFz0rCQ~1V3ueWJl(Q|{9Fz?)mQHmjeZi1sX5>u;}@}0jb_vIZ1f(S}_N-HfvzA~0LR>av^m4m5_} z>HXTev@19}`K?1B-5!F;1ht~)4X>$D6Jw$&2!TB<;t~L0#7`@>HySM!PouY!@N^OA z{|zK%8jM0JOK=U8QD{~$_5y$}e2?)dL@C^gN!>?Ca{4WMotMi#Fzy5Jw{``k1T3VP zq)O!afahQEgRVU@g|P>{pge;KzAJ1QO@f<^AYl&U(}!F}%K#wF`D>y&S^0-9KmPHO z_wyAV8~L=)hAzk0`anE<+xm3H5=FbG50K)jT5T<07D1SY2&$bY0dfJK?gpR;K=hS@ zW82-J|4Tw-#{kjmCHQSfimM`_Wh?K!_Xb6&1UBRO!wL$|ftZrC8t12@+J<*i9P%pO z$;bTQ#3Mr5PbsAO#-vf*(B8iBo#ucfwJHoyG${m1`}qm|JU!u%PB|e&x1^A^;pc^v zfK(lug3YvYQHtz>ix&NYkh~LTfRwY8gq@Hivyw-~NH5pb#3Tme6iCn!_zeOlOn3`L zQ2~G;4G4MbsYb`guQf8}?N=d*gS`FRLu$#H6v~;ST#6?UQm<5$+N}s_o0%+FK`?gK zn=>42lYmt*rq2|QMP@2{V-xwO&bWS$?gl4Lx$N|l74N0)T~l2`g^EkSC{vtBAqf!) zN*Dp8r7TTIQJw*y9!7?sNa?bju17f+uOd)1jMY3PskDY$uMt)}JT;XTAWJzwWJw}) z`-ehw?z;fQdMD`rsWa|R3|0IIpP&5!5=2^b$<48*fzUg>XQggcC|GA2&9uyG`s;PI zx2z_!9GpM|RS8xsz_L{FKx_v09R!i@_2=og=hKofmggb(yV)F=|73iBc z13NLf^SI4VKEQcpFUDrbQWZ_U6|bxUAdEp(&+o48+zBMC#gZzWvjQZ2n&h%$ZEX-t zF({Tx15ddAbPvM%y;4>dF`lgi;L538t@y^CL`YYbT`N(c9%C$}P&~$M&Y(OwU3&J> z?wJ>cc5DVlutXBDi6k{KLMB4kU?MM=m=T^B7eW?%A<8;OZj|a)L{Z`)464MS34|!3 zxa!KNwWOt~2`EDh3t5`x(rr*^;?r($(#hEm9n0sR@orks>s6G&PA0OCQ4+aSQL380 z@ittX|AYyDpeXts&VcW)hO^ydjU(S#07X! zDZLA-5ygegx-aSC0aUVF04GIp{?ok|Am?*`Uh(HJoB?#s$Cv*XS~&n{m+Vas36X|GCMmZBt<<1$}Uar&x!ma{aPm|J@Z*Qyh@ z)vGjmt7X*Qpwc=>MCZ=awYsQ=>z#;ksVE=oW!ts^4mgfTS;*M-AOs?3aZw5}S@03k zYBf?91&{%g#h8lH?+YvI{{(=oWwL+-a3Vz6k1az)!u5H@@w_Uky373<%I{ukuQi`yKt77Rb8`1e<>Y)$-#uR~&=zP{ZqP3+Y z$I*h<@%Z5WSZNSOam!^3;_3M6cTrlmNr=;jD#0+5;IH+938|~`tC2RbZ#?(>a3oyw z?XYoqqY#B>2~N)A(sgI-XaBasCr)nN3cS6)Q8)Car2!vZ2Be^S!C*5XN`L@!jFiP8 zdB?~8_H+m@dPY$i>p=u(F-lgZcI}#7QMLmpAJ!xx zngB*%wm5qI`t@o^xom^r&X_BYok^#=gxQo%N9g|1w1z6m z0)Vp4IY^8Q9Plw9D0HJQvOgw7J1BX@4ul<}B)uF<>p)Q)eZui)3C!K5B`6) zT-N4#N`RdOR6?7O_6RUOkjZ2KVB(zXogN^XR8s+~h(a-2i1&d=w*n!^NK46`ia+sR zlGJk%=>HcALLl4w2V5b2SJg6+?b`wE*l}*QcX{Ege(=Xrq}^AgnwecgQ+hcDy8e{{OM?VQYkM@_001BWNklpLobd1aKlI)M-}w~>kb zAZSHD>B?G7UH?fbmE~SA^>3b>dPbp2BxT-yy)f{KlHD^zh?fQ=8xhjxDmeb9*j%xQ z@yXrQOz-!gV%M&0hFe#p4TaV=glXe#65Q58x$FM%$-$$lTH?)hAN-`mJaD{2j|O0b z6%xByp|x8mPA9@7@<XwfeqQS%erYs^nmtk?Frc?##LDYdF~LJ+nbe}+$Wwqk&;S$}ue zF2Hjx$s;N3lBH!9#m!Gsy}EW}&Z0R0QJXtaJJhow6J+uD%+v%p$?@ zyAW8bEjq6UDf9Y3XeDZd4M3<~NR*Va_=*JYbVOfslfzDbtK=LxVd<;hOObh(p=CBV zH=Dm08XVrMYXXHM`mHLZT4|=?3%wKDURhZ4cWaXRz00p^81usV+Bsb4j@4Y3W&D3R zmtK`E-tn;q@-#aVEM3|aoC|zX0_qx{pU`N1hKn3W$gBlm$S`_mZRY85-wYwt=m+)* z=~vwC+X3DAdsPvPNVSCEpa=E-`S$N3(Es}h^ZDek0!^12;40@M&7y2m1~i`0wD{r{ z!|jq1*tt5GwzmT{=ZB+d?Vc+X8mXZ&f+$sUQU*gh!j#}llNP;sCBi|XZ2w?yH0;Nc z`qtmIz5dMT_=`VqUC*wq-uOqD#2XmP9iDccf5gzc@Pie7DbvLpJ^CSCYx= z&Je8M8VjdB5Lwc&won>BS}skrTcP;+P&ECKzO#d0%7-5*N3F}>qmfz*qtGN^ekce6 zP}O);C6W0N^Rp2@lpXFk^Di&;9o=)zVw0%8Mnya)0q$ z%zHY$5gGcDIw1-sLCR&J$dZ9Gx$7p|`=9LH+&uWk$e*1ONP3_#m56-&qf)Z8&(1zD zQEER3zR2bPysBcaC?y+72Ca(6lQ(bC^-JHU($*puuGcrz{$M;>e_2&3d`S>g2DAC@ zuLtPlX<#WmOxJ8qSk)^r#!Fo`L=;7>1tIzi_KCkRmB=_~C@hhwAYum^UbL7=IFn@^ z0~=NhzcEVLE@x08ssa`a0Ac`8fVQ@y!M1HMcKe6S$D+~8@=~I9#%6k$usu(nEtHB~ zwyHEP2pbKb0tgoSY^0Qo&iw+HsGD=%mCJYbnjv|=sVu*m(#Tb**n;;uUaYg?j2u>} znOV?u-6uo(0t!fs7b-9QpeQ=~?ZBzPz#t2US0k&jPOlDqq)j35%jVbp*$Q1Le}9`i zIP>PP8A2d-cR$e8V6FU$Wj42|B$XVg@L;y9cW4Dr)k`#Bq-s8M`sJk%OdS9vR;xFG z#zY{dhmDb5ej(}k1>f&_#|{vAx32(_*#=!&y?5`qi!nBB+Q;*ShmT`$ClONC8f4xd z=%ji_gw)i((FIikY8hP+PsBfQ9|C1z**o&^WbyfSUDGR6jas$gbd*Kr8884aAz#Xm z6hHT%5S1xR^c4m7K(?uV{d-I^GB;m1`?K-s&PT$bSal|P<0tFW8^3DlOUfiXNpX6&s#Y~vdQC_wGkx=_esXA}bLhpX(}!LbjP?@sx(PkiggNV3bwZ%X@|J8f%k(^qw1gZuqT?x|2=blbKm`*pI*tfBH(r~Aws@3u_>4v#KK_$*sntQ>rH*Y^hf9XkdE0HBUj z(QrszeJet$pybT=9CY?{A?xom?I^iSNpC?g;Ur3zDBEVX1wqv+kfRrl{UVsKE1A` zT{PRy+Yhq|-npB$R1Jk&Q+c}n-# z^!1VOZSS(da`N86$0P__rRii%`l{kLy;wuLWqH}slES3jPU}( zc1Z!%r6rZg^n}!!Gx)_*3$2bb0e)d9iw!9ev4;1kBC`pwez#>A$6x9W_3t@Z7CI?d z0Urwpg^h7#>hv>R;Zh_5kau2+8^%f3dwaSeYrE*zfyNef|}QxjqPkgBZmSr1j#2O^ee`0Xj@05)Oh`Byi1S zJXU*$ruw0Pbsh^_5=rT-MF?-?sPeGNNhG3N{$5H_e=4OvO>6d zQ?9QFT@5>k_~WW#;+mROU4t%YW|!nwL=+(MZikRjNQIarB>3Q_0>i=WD}$SGP&-uQ)>uT{HVFn2y5U8!667a zAu0RUFjO3=scUMa)L;lHSjd?q=cOw#4v$34#UJB3D{&@WE38+f#HD7AY>X{i*Wx8L z6e@A>%aUtOp*&I2Rm7weqmEa}L4?|bO`S%d)|Z@pgHdZ1? z6?^RLrcJ;PkBF#(^;H1DS&vN&UugdMMWDY)k_`MHm_Dtm)r}L$XA4639;F5{)Vd`I z8w1jGC=p01?63S5_LV}W5H2{)=x=2y5DI_v=U5!g1 zOEBmNV^b9}k`S_#Rd6PQFii_#b0L{8IkANVn-blnBvDaFRSu)bv;;DZAe2*S-ebQj4BvMh!+e$1cBG{qgfd9ojZZV$>JJ}wPiw-9;;Mv5de^ZLTQQ6@_^^| zf(wNU%|E{g^f$>9PXNE^Lx+3n;&(5b7r8mUP5!hpTV+%xv{BXZ+^~dVnV@=%)hV=V z*DhFj#RqIDJt>9Shy!$tAZbeJhcVJ?RWf%e#WhmU875^g$%WI;d1(>{RPhPN2+1@T zBQK7XzxNcf3k>s!5=tc%mthriU)2;{<-*XBq#7lGDv-d80hCLgf{_LD~S)fWxCksBJpd@nq z&hG?)C;}-*gk6#y7qj2_l2p1XIT zQLVA+FOgCvY|2&<3{G?P$1qnP)P-tT$9DV_$j7soq&g~Oi%!~(o)M04C7R)UZny%4s zGN$NAqat1nNK|t%Gtf0Pul&)rqX9q>0E$S;*J`T)UMs!qOat2Vre{f~0Z#>54~euc zNG6Ocs9F|od7#6@pogr64U~o#x!&;;1w1lXMNif%_arrBT zygHCV4pP-@3h7JMxZa89b**s&fVWOUu&?4D|67}T$NOG9>ZM!NZ`4&A_}KW6yRLqP zWzd>+AZ0-zrl#Q98wHdKNJi|O`}mXInc?DWBpd)BojMUpBUggpr|i;T{}WFf<+t2Z z6ICLa4MD)p7IG&~Kl>=Ki$K4?rPMv zlPkD-E#Yne0HBaaSxWyHLi&e^NOf@T6fiM0j3d_&Bnn)X9R*ZQ^|XpW$qHV481pGL zY;L>-fX}(~_)x|geM&?+1zc+4|vpW?Xuf@o@r7ZLV zv9OzGTs;;t6IX|eX%ZG*vi4#?k)=>`;ETOgI354!N2tQ*{fZ z6iWj(`3--jP+YPEkP>UG!6?!u#N^X{xR86NM?_r=ul+wLn>PcWDLyueihIx`i!qA)}E%WIKecOe3s(K*&a?kS=oaT zK8itq9t8WA=b!o9aPj;99@ehCN~MWaL16!!6MjzrvAutzetP`chE(IHw{E=dtIfaJ zCv9^xcs|MKXNyD`f^Xd0#^Lz*(s20dBGCuYsLD3H*3?Jj%e%ucB|J-&brYZJ4 zCMsM?T~ZQ?WI}h{LP_cxA-%o%;Mu*G&KY%o+d3|@RKvJLSLWPj`$teIINW>fdlpXY z*m1i2CdHR0i{+U**Z8;Uy70Wxq?gYB%;NfO02L!8g2_%zIUv!^U~~o2dSAtq1S>w=r&^u1jESl^^*Y44N7GOIN>Q{BLGGNC z(Y}k2d>DzmvQ0ZVc*c`zoCA4t$dw12EMz>i(@YHwwzL4BI8SgB*;#Gsi6di?*5fr7 zfRP40EqE&qHePIb^o>cmUs;*uyuw>o_l$O?bLSg20AKgDeGxaB(jf@Se9n`(h~veS z)87CD9^@RY!ASa!MVNk(Y)Q0G7U^dYX9T&kWFfx{kZ1=Eox{P6Pba$MILd#A#X`-^ zORP5E#5o-EZ*#y4fhR}p;AG5aO4Jf@hWuUvQX8(KgP_kbqrQSt==3gn}Z@wAQG8 z{LYwp;!P)6;ZlUWsw4zD1te3KlFMpp&g^pUYxh`PuF~ZIOit*nFVR4Ee9rpuqAV9q z!CWXe+1Op4>}2SC%(kap_cLX55O5~-#W{zuOjp&+{JCeYlmh^cqDfs6e0G9ejC0z; zfyxqgUz|#vc-iEkA9FAt;~*#)SuYbv)=eVG|1w7EG?NA2h$i>_1S4x(ZsxyT=Pk2l z?K(Q&-ZVJX+SpO$Z0%ZRD^9%bvQuSC}Gf2HX7Q4rK+2oX_JdrZO|G_wG!5B{=gnWSrmTwAzZ8c*l zF~v?iJxs_3%NV*MRj6yV!N(FiiQwSzS0~hDiOv&k8qBsP##hQ;$G^I zr#-$e@4pT~J&cU2sXDB*B$1}6ysGLTJ-zUD_ryC1cK%r=u@w^qv`d}UYcA#lnsTSO~`uI~SqKUiA{`mUo#+K905MUS&+dW^kt z2xH}dfZX{hltdiJFksLm(<(?pzK`?dE|5^x?Ol8ahEyQTez` z*Rrp3PD96!{r1t=A_%QTJ(P0@?bxxT+w0XZ0RX?>%XaKI$mU<=Q^ys|ohJ&m?^;my zGdAgSR#TB)tEjlH5ur1O4-E|d?ja;|73GO|EYkXt z5QX^pzNXWlpFx*S0Db$(NA^we-o7le^ryLX{Ks8u(HUcPcE=q%Q!Tyn0bjgC)doWs413ahty{*)?O%R`R#b2MFGVVvdT-S}$!m%Se9LkL4m}Q9= zTZR!wx@aj7Fl3I#^j*7hy*Et~Kn}JB?s#Qs@!*HfOz++esw&Q(=u&6hVwh&Awf*7y zoD_m9-@0$7V&r%BjcaZ$mvpBzUPO5}F&ac7$tW7jP zSp8HD{N9(s{gH>B6mW)LCWUspXSo9BF!ra?=Z}RWn;SH<>t2M^OsjIiwUUyRL6}{8 z?v8`BwifVPTWSd4)b`ccT+t01fMv~yg#wYb=S@QvJ9`ccrHtU1+q$gMG~=8xU02|O zY!3XmrYHOYWa3!7uK~J>V;?qQwYw)@br)Y$o@Kk}y8@OIy`z8sUA~g^pQXSB>gs?b zTp^#wy7(IylOn)UH>Z|kojpJM({Rgi|JnAuo0~zhu!2d9n-?aNy<53?>_k-+m^K}p zPDlO~984{eXxn0)0@3tpK96QkeP}Dyldq2V{Bjwk$pH=GnJs8aj)V@s2N7 zi-Kpd!A1`RBTqK`z3@o*ZB=CN2@>akS_aAsY?cduiji<70Nbyd!}l0`xD|mFrBY*D zQIr@O>lrO8JCSYe_F|LII(6J_w{W0W zajtcWI5(*oJ$Dtjf7W_7mz{G!drw-k9G0pdFpBbehYtN)>|~_NI}1dMIdzHMmh&^y zOg<49ef9q88E-pypLX5TI+ObL_UYaWTMj$q8Re$q61BE=D?OW~+3|3fJ2Jd)tge{_*H*UvHvo3P5|S^-l8kfgCLcS5k$b!>C_j;* zFhG!MV-n&7+1riG;D6TgpS5g2z&HooANjd^w6j&4KK;?e+ki*=+Q-}TGnOt8N#=Bn z=vdrr*%Xhr9t(tmkrIpD)SZ%O7rs!WO4%17qUUj1JlNKzpM2L#r#D~5V7ttnKVg1z zbNN56tEp+KCBwb2S`jkWVxTR(f#2WN89cnZs_59z#nXr58#kUX{H?88%f1Ir^hAFC zR}Q-FvCRE)+@6v1?k&5sdN0jhTamAPjx108KIf@K)Oh9B1BqaF<$Juy-piXT%*`bP z*`2a5X*zJ;q0yEtL(Sq@a|DfzAZl@6ks^84P^#?<#sRJ;QP>W@T9pgQr8Qv5$e6fX z5Y(CRc=$=LFVI(=j?L4ce-Kqxg3&y>W)C5Z&C1(Yd4J>CVXv+Rb9UZ9a47NM{e43l zU*t@eC=1I1cHWTTSblrHnlZ8MZLl9aSS93HZ>&aO%}VJ#_rzkmwnU9B4^tMs4}c7E z&?CCh@q8%$w}%ovqob$Yf6B39(*9`_`sYX0Q8(v+>NpURMG7V2`66YZ&attF>-*n` z2lj0|Vbs?HZfvX)ul&lgoN-k@o|ii#Dn#4bLS6n}{Dty_>5&&)V=o+MJvYd~=CV(i zFG}NXXFQRJrC$5hK(xJcLI0rQ{sUmd z5-qL3WXGcHZ#ruE+3y`fz)U9cY~K|HqBNUGcGBy&H8g-wR|kZrbb&(H zim|vp%f5WozV`9<+|dk+U0JXKBf%3%?SCrRt3a>h zdN^Sox^wJgSNq$$;l5Ym?fym(8yY}3cygR0$Nt?KRk~mmAWM{CxiNTbZ0!B&>KA?i z?31%=R;hAkIRJfBBttznR)JY8fIHha{d!rlxl_8n(UVM$p09^j(lPqS;KJYExFN85 z{H9o@V@c)G9e>z&2Zz*qCf7KiUB@Qcyw*i?5VBSx@VAL#Nz9~ut&At_h{>4*RLk2v zfV#SyT2KF+w=!(JuFmf#vo}5Dwq1rWR&GWZ-I`@7_Lyb)M{~=?D8mUsGmrNvBwr$e z^Lo|lX-me(0?3KxnJjcf=YxL7MDDW)<1dtO?iG??A(~n880Xq$1{*3?mCReQ34e(& z_J$ET-Xl4(!i0D;JcWDf1^b@(KNUE{H}saXWc&r)ir~( zv~bg0023N_UB7ST%6FbwudM~V{L6*TZ1s}mvgliYKzJ=2eZ0Zt{KiCT9?wU_zK-$O z-eBn22WFIXzi$OvT^*2dvoo`tw#8q@Nc;xphD4AzDhv5h4$RQa0g0o8n;@Hm1KY_N z@4-k}mefx^2%w)71m|i5*acQF zLl9kG1um#by=9{;RV+pbbtkm`r-zPp^_|UK2LNc~AdVf%EH21d@ng;**f#js|J6HS z7|MYZStpcx?bZK*5c3a&pQ!C`4E24K8vQirY5n7#ZD|8(X7;8zoO5$$@3oC<-^wu8 z)&gGnwOqGXUAY<~*K$DG_F(9_`llc6jx1dY*j`v=^?BCJAjHnPLcchW$G@rg{ZGVB zyR5Yw@CNkX%RTGrfD}}fWO^MluK+}+a-c*e#a01lEzoJ-o4VQeDu5Ctf|#VHriwzw z4dGbJ<5qRTbb{~=4B~?Z8~AHN?`m?Z7v07oX+)EIUQnd`8UQ?*;EWDNQ~zeNgxfU9 zq8$q(SZ)o?=L-J4uv;y@^ zP_qm3F8P5Zcq`*(d+kWPVgK2#Ck=X9A4s*efUo-c3N_by;{|{r)^ecc$kL_2jt^9M z6m`xu80@Plk8Te5pZd#lj}5-P*#T;71OcE5a&lHVCD)_{TIyJ5py#O$(;gEt>`TXm zBb)mNm_7XNq!#CZ4<66)*xXf@VuYtzq>3tn?a5eb-!EgarY0K5)BylgAI-B_7u~HH zUAs8&K3T}USRk46IT#V0b_`>1EzsHEMhWL<5^PySdFX&?MxWG@?F~+Q^;(m~Q}Or{ z4=br~VssSns064m50HSpC>HkPr0B#D51Zfz?QB`?+FHP0`Sy=05XQF}bfAcHodH-5 zNn-As1iWKoy}#T3e%bk24tUNX$SCkG{~8BrYADqGJHgrV#=HX1w(kdz+dK6JRq1kR5IX>#oH`P_&+L!z0 zYePAa(-sdQU?&DI#6)xzU@)gso*Yc+oe%X5w>MT-cE?W7Uw>m@e8|F0za;vm1(KVe zGR5FdE1gt{k#Z#g&uT!lkD6nzNXm?<6k@w#$*vc4O&nAdpHmjbPXMFqr2um!tINXg z%AWh*`t{scs;Ma#Mzsfq6g>5Bia2>G!8T1d1JB3wjVI{vg}kDo)p)4 zt!c6fEjhT6qLw!o7lO9yDA*)h@p3}=nq+clOCZ|v{O+gH6>w>b{y!D3*8!Ey^LVX)w&sYX*BxmgWU)G{H zvSqwnST#eezo$O2VZ*6Pv>iK^;H67UPbjwBPD#jIjJW(JgXA7fOI}J%cALYIGuJBkZs!~hAq4Ey zVm=b&976AI$oLPt+v?S_CUx#2(!_NV@q7`fbVxBH`-zy*2^{?tMJ|V9-d7}CcsmFExXD6~ zCQ?nWQ5uUFI*UeoXv$ueiG`$Lb2z3=R^=HtVx+RD_Ucc1TLwDcFALt*HeQaY8HF}^ z!eUj-UoHtnWdu71Fp^W8Cz?6XQQ(q2nA-ZRfYrD`V`CeCw=wp9d)u4?+A#vwV*GE* z5s@mBM)!fh*u%RQOoCWT3y9rudvRg@%pWSEB_|r|{y7_KJ}`4;x-cdU`bPo1zsizr zz3NLC+h&c7{rM+~GQsXpFI+*8v#TR?`xa9(ZH1X@)(P0Pma))F{^(H=O`2)su55F?%ZypA# z#T24k&CgpoS2ItB##!cjE2ECY%F)y{8~M-*(l_0G&<6<1JL>@-_KUX(rJVn^myMbyZo(? z{2=?fjg26>3a>3j;8?}6HeSS8GG*pr2JtWg95c<%uvM9TH{~ITvQ!H~sF9n}uI~7= z``$gLeO&jXtPFeg6{6s}5;(3h`IyRBLdT#|f*mP9(otZ>^Wo&dr~GZk2mqMH#UN;! zQ?%xI#YhA|8)=HpoO#f!9Z}r5))~*EEE+YPJR{E_igto>&8dT zP-Ji4FM9GF7n8odcj@Qy-RtUr*rtQEJoEotL8)O2#*Q>Z;rXu9TV>li+)kf7^ZN)z z*N4J^J54;esd`c@0C4{HfJ=k^F@kOp7JXeHmd_(D3kj75rHe+{{MI;gN}sC${=;_uvdxBg2hET-D^-x zh+Da2Ip&HpEI^3cdzz0WxSK-1tA!`3M<;$%X|4sDL0h9o3%v#ovAV{1x?pYHEOgkhV>YjUWas zxsLH!U%vt&9UmQjVsn%o3#@(H>9^g@4o^nr(w`z?-ax7Ta5Qz`&Uj(C=c9DONrRpi z004SpAUo44R^1}uteY9|M?>MQcXKT1nU0mKC7gc^gVZ*E-Sacg9p^JZn&u5XW> zwq6Y!gk*cRd#d-=&r^uI0^tK&19VGE@F2v_)w-XqgTMUpDw0`vapqw30hZ(XT4^Zw z$_Zvka=Tbq$(UA&iF=-a9WlzHFA$R36$(ArITBV3ANWA^O|7(Br~ZiXL>A`>jnXh+ zVkzMa?~fRDw?|WaAfzb_0H7jkO`c7f@n4)L7oEx#stAE_(B!dKDGPfk9rIES1G1QD zq>SEUl2C8~W7-ho$^A$Ce|df72Rz;i0H~o8J8u;P!LJ!2>o!{8f z26CQl)*OXo{eTd8A?1+=#$vDiRx3{RrD0El{-N(lV@H+4?VP+EBk5Z@4YY=$2ksUG z5_4HESuG3RwI&O0562(>GZ#cXQ~mxj5ohXS{X4f!p8V(N+vDz_1)LRyx2$J8QDM-L zmX4F#pYn;?==}MheNeZPb3pa=fNN@i)z<@_wc^_a5-Pe}XFXPoq)Ln|PD-_%ikKq< zpwU>W>4Yj4&IUkp0SuqXquHD%G6AF%ApSh1+OsB$`DG#3E|UrzPQKjju`d4+2cwiA zHHfe^g19gWz>6u5=j*Khpso)-#V~FUMUMnBZHwK0?d9Q-cz^VRLS9z~#90Q)l;Ynk zqKIc=ZVvmCx9xuFK1iM^VfSJaSTkKy=Bl#$Hi1|caK!!;iyr=Mqj_&EQ?^9 zfVsU<61_IgO*i8V34$ZW8EVIvP|i{ooeuPc6Af<)5()IYSs&lA;}nb=Hk`RT>zVHF zmMtLV&bZ3yQ!lRu!caK2ci*7)z+iRB`zJ1&n!uW}O`hv;H$*ey4)?+30;6t3JItISG!x#!AKgjoQYVuhH-?%xdn0qTmj(G^gdxP=A_qOldJCr`m zk_P=8l5aQLe70%J3G&{;K|3Cbz3>|oYjKCL=qge4tTSn7yFaq+&oWPOr)$NH2-NBn z9sX-L^5PTq5AF@U-yU&W8_3-e#h05mZ=EdW)L<-|ltrG`=&{XaqOUvD6^?xjF6im! z$$$pjJ83Ak914Cf^ho_i)&wUzS$~kNU3=<#oSx!( zwmx^~`a)XzC6G##pXOMt6;lBaipLw@?9?6|Tw2_n`Y``@V}FiC6$`2qGT~McXU=0> zyJs}=!k6o|Rxg;x@sc`XNR`{?L1 ze~g=QyuiC)J%I3egAG11l6d@yaLR~IQhx2Y@4l2lWjf@ul``BUnoFs-~#Yr0*f5 z&NtcEAI73vADUg97&+gEHKaj5lZ9xk5{o8&VId*;{~FT|-hs^MfFze)L6GxW%C%R< zv{&vMY91VM6@$%}ap?~k*VJIN{+Ff^i{@wj=VFW;qlfpc+JCMV`P7gDXO2smcLgE# z)dX8Fpe)!znYJxu4!p?m$>TvS9$8upsSj&SPgzo@BinM}90Hydgjnqu#G|@l?6F&O zBA9!e3<JSwW~t?X=d^>EZL4u)TdQYckVL>J(KUGgej&0y zIddkMAJ+?I_ij+-ywY;3GWj-1@Xnu+dQc zll28Jg*JQuOTsze?I*$RQ%lDyV&&Bc;4*~7GS1lv;O46a3vEyFc>%C9pLhEAjCIc zc9R=QFtHX;Ffl`s;7FjpS&Z*Y{7(V?&iu!CIi#=&Zguwe1Y(;VPqyFdkM7*G<1rXZ z!=48H9TvTK`qD~=Sbhh>D39{Q3!{lg?;C11M&5qFqq%vB&5=9*bAn*M4mcW!vDSP0 zL$4k^TT{eye%UmGROGVTRXn~#6^oaO#9a*_RdLX4lxzLKl1-E)518D4Xvp8!rJ__2 z9Fb(I0E``X06V>)8yY|`J>w=9_%8SbW%{02=-GR>OorgfuJWu*`>Z=6(e4b6V)j(Z z9Cg^UGBGypN%8Kz9%;=oQOub;YCQR?Cp@q9zn6D%dM_W!f#j0;a-nCH&tfSmHkhmu z;HGH|bRX&ad3T06tiRvO=;PLww()Y7)hMwEWw$a$=W@>Pjj_hM*?G~i^LbqIX`T1S zRBLMi=aj`N44XR#LTwi9|zChr()Jw9V?wdP*gw(*%s>181{2z%qpKD#D%q>D_3X|(&(~);(9-Ekcl7u-KzFLP7Er||Z)&%B1E9_n=$8BS`h_?=c|$*r9OeM`$8SJ^!*IX{Co`neWnTB zCcz!?1nUDx&C~SI@pvN8f73iLwzPq4rKP!uVNnwsGGTN!HiAzRTF2-c5h!9D?UF?C5-kyrs9?ZFgL{26OfECJXEs zi`Cs5IT;vvJLJvHmzYmP<-95L` z|GzqK`_#DP6Da^laSq`faG*DO5E>=Lp;&>~NJ_~hf+b7F+!=|6EG|dEYC)7u0Dw6! zfH-B&kLHd9+QUObeVqj*>{3e2up(!O0IGsP#)MeepdUc>B?ZdE;gR^u2U2GHNMEFn zdi}tbE(L}FIL!ZaKvG%Rs@F9`?<9!aWw)*Pjw}>@wa|Md-F4*YXW#11!_%OD<|zl% z)V0K!>72a^BkT1B9q3G?UfXRIrsvyANuhx45{#*GhDTK~Q5_;FSwcWA(rMQpG&uV5 z+_DqVf2JjSYmu6#K9K})2-ej@|G060gBeA{AR``CnK4I_QE5WASc;~vlS~TElRcZe zOUAjcr!>L|b~+e0;sWv(F)+6UV{f#Zs!13!iy0q{@nlnIerYiIX@1OWD?x8;Z0{ZB zk-L2M%fD?A^S>(OTuFCyJ^egz2*Y^>`}qbsY74Bl+g#I^V2%-^!($EGXOa>*6zk494 z7@viQJ#!`)jg7;7L;ShBeU3}NE#d61dZm@Lp=rwt)#n-P=M(7Y=+V#FT*}vBV24Tsw?WO|8w391tqUg~jKQa%qsah;R0bPr zLP)$xK=y0Gv7^8BxyMYmt2AHN2b%;L+nFIH1T{O@A9?Ps1dH}&6^>Ip>at}RTRm~T z>kWH!Q2(qv^tWK|ALN_v_Bq!4i$%%*YEIs2)_C&Ctup~4aGrFjId772O@r54bJTeY z1i^EK$%C!Ekp~}#TnI;Z*tp#uLyD3_J(7-8(LkkyqOma#PIxRV)gc@2*DUdq7Mkoj zrslYv^EVKJuGVzzfxeM_kC*1IaGdCRtuH&P!X44V@eC!`7>>T&pWq;l&ssjmX_@*> zYHGW?V)xuvT>_!c+Vj7^5yVMOB|Fc#_S=Hsn3^0eRH?NG5`PwYe_^wRGCQ=Z6l#SJXv3`_XCxzewwtU4P+N9o9S@mZ4d=zmTrch zH(B)Qc(m&<<B0rd~5f<;xGIo<{Ht(JnTIOiQb!H56LKOs4|_A`36`~F4{r#V!g z*R$?hz=bJsz57pz__mklmO=Eh4*O>w^xZu*YFWl5Q!(;h3{3Cr_W$*{ryqVM@>ws9 zY#Icsz~?R!1=lwa5-ws)8>NgMB^Y&5#uAJvE>+4ZA=rvJPrlq6eCE&AaB%2jJURD? zR09V=?p1x+)-~S*Zm*==|9}avG)(peVxMK$KhvPMwOt}RGq0E=$py<0qJ1X@AAPB6 zTz%wx*-j_0I?Ee)d7(K&R~C1_is*ww>NfAhS1vcq)SUf z0|<_B8CjW*>%KvdbFxPJ9~>Ily=&p*Dk!%wuf5BffTlTtcpD|15;}@rOYm1 zMK^_rKNLPXoWu}aSq3_sXRVVP8bGj@WO}n*U%wF}bwbkUdN^wA+cQ1qa6AqAKNoOx zaA1|HBFXEr%$P3-nR(1qdjw(~!Ng%;X2kuG*ZURoL@W{knq9KYi%{tTU^Ltt{q?Tt zIolF&9`^y~98f&~4ju$t76razDXwr|wpXBn`zkWRc&PmWik9CJ%`Pfb-eiDQ~=@-~CaBI3IJAnP-7q{D7)32~8iVY7&_w+oXAyW!n}OZD|8J4f_Amo7PIQ3ovo4p)`89H~RFeRpSo+ zV{2^vw`y%_M%FwxE&mrs{L?K(!CF$of?dZ(3K4-=2*oL4!45)jmZ0K7!9-lZNW`2|P8ro@ znF|u0^d*dx>~Q>r?-gd=H2?s-p<#)TIZiFISxc@lSv;5V&?Jo6JcKMGx*7T!BHeox zymS#k*)s#-hkrYxq&t*e#%X?85?aU;siS zp27ft00HNmrw~9K0B4yr`9e(7|B*Qtvklc@pLz}DiB(!^@O40ZKot2HMqI>5$mRf` z>+yiiYIh*&l-_!wrL*p7ovRzq0U*)PdDF|&v+L(h^W9$QZOPid0qXVh7IXkJh7mnM zxu!Enco8>L0FDqKV4Nbxbxe5*ac&?6fDs6ovJ?R@i6FQz#tH!r0$@|%j0&fCq)E=r zFh@chb9?~+V2Azx|3pPfVT2?bL8L&nWKA=TAO)mRgpq)#g<#}FoM0JqY!Qfz(@Qt4 z4`aisOF(OEta-ht=xJ|#ef_6;kmE-N9ihS`06cyAk8d001NHTQyk05t5KeN2yK`wJSNQMV)aI#_}|3iq6_v2yWN_ z%t6B%rRD)aur0w5$&>_-2#8w{Qbs7JJ&CwPJdWa0K!P3@;bWaZlGgd9Q?>tB4t(0b z$H$#y;OJD;&o^v%ufOho+wX_Zlr#7Tl)HOs)KaCmO+?;xBFdd#bMums4I6gG%F0gY zNBvv3L2_bH674Gqc3y&z7y^#B#Mv9I9P3shIA$Zzk0nBuRC>9lb-wG9=GMN$^7iem z*A3(N5tELN0Wz;quzO->=Mn_~_+;<#m9pF@- z?vyoNYPVH>gJ8#M0cSWdNZk~Yy(SCB88d7cglvRJkaM~voY=mR`-}Y_b{<+*rqe!uk&FwLAtY5IAQ_ZL49+9H1`jrxX77um+Q`wteLazX)@Y!#(n^E= z5yd&6uC66=P^NZ=kmVwx%a%%oDP;p;J(UR4fDlhx@wC$Vl!N|m=-+nzJUuZpt+dig zE3LHBN-M3j(n>3>w9-l|t+digE3LHBN-M3j(n>3>k3s(rEyp^&vnOcU00000NkvXX Hu0mjfpgHwc literal 0 HcmV?d00001 diff --git a/site/index.md b/site/index.md index 556d3bc..2b5475e 100644 --- a/site/index.md +++ b/site/index.md @@ -1,20 +1,136 @@ -//META:title Hosaka Corp - Shell Distributors -//META:description Index of Research and Blog Posts +//META:title Stagnant Project Index Page +//META:description A fun side project //META:style /style.css -
0x0B creating a PKI for OpenSSH
-
0x0A hosakahashi: takahashi in pure css
-
0x09 using voidmap for penetration test project management
-
0x08 LD_PRELOAD, hashcat, and bad ideas
-
0x07 RISC-V assembly and shellcode creation series -
-
0x06 systemd user persistence in metasploit
-
0x05 talk: ipv6 for pentesters
-
0x04 Go+SPARK
-
0x03 wireguard quickstart
-
0x02 abusing systemd user services
-
0x01 unicode rotation cipher
-
0x00 tinc 1.1 overview
+Stagnant +======== + +!!! Experimental !!! + +A pipeline based static "site" generator focused on allowing flexible generation. Fundamentally, this project is simple and only enforces a simple set of rules, filesystem structures, and a small subset of exposed environment variables. + +## Demo Pages + +* [About](/about.html) + +Reasoning +--------- + +I write my entire life in markdown, and things that aren't in markdown get generators to turn them into markdown for my own consumption. While some static site generators have a common markdown backend I found that often the generated code was hard to pipeline into other outputs, such as Gopher. + +Additionally, my CI of choice is Laminar-CI which takes the opinionated stance of "just use job shell scripts". I found this rather freeing from implementation specific DSLs or janky non-reproducible web interface configuration management. + +I wanted something that allowed me to write a simple step-by-step sychronous pipeline using POSIX.1-2008 shell scripts and allow me to write the pipeline steps in any language I felt like programming in. + +Versions +-------- + +stagnant contains 2 supported versions: + +* `stagnant.sh` - A POSIX.1-2008 shell script that contains all the logic for building a site +* `stagnant.go` - The Go implementation that is used for more programmatic logic, will eventually contain an extention to load Go plugins + +Example Structure +----------------- + +The only directories that are fundamental to the creation of a "site" is the `site` directory which contains the initial format of the site and the functions (`stages`) directory that contains the staged pipeline pieces of code. It is also highly suggested to create a `util` directory that contains executables or scripts used during pipelining. + +My personal project root looks like this (directories marked with `<` are required for the generator to work by default): + +``` +. +|-- README.md +|-- build/ +|-- stages/ < +|-- gen.sh* +|-- prod/ +|-- site/ < +|-- tmpl/ +`-- util/ < +``` + +``` +. +|-- build/ +| |-- 1581996075/ +| |-- 1581996127/ +| |-- 1581996133/ +| `-- latest@ -> 1581996133 +|-- stages/ +| |-- 00-depends.sh* +| |-- 01-slides.sh* +| |-- 02-http.sh* +| |-- 03-removemd.sh* +| |-- 04-minify.sh* +| `-- scripts/ +| |-- clean.sh* +| `-- deploy.sh* +|-- gen.sh* +|-- prod/ +| |-- 403.html +| |-- 404.html +| |-- 50x.html +| |-- a/ +| |-- d/ +| |-- e/ +| |-- f/ +| |-- favicon.ico +| |-- favicon.png +| |-- i/ +| |-- id.html +| |-- index.html +| |-- keybase.txt +| |-- keys.txt +| |-- n/ +| |-- p/ +| |-- pub.key +| |-- resume.pdf +| |-- rss.xml +| |-- style.css +| |-- t/ +| `-- talks.html +|-- site/ +| |-- 403.md +| |-- 404.md +| |-- 50x.md +| |-- f/ +| |-- favicon.ico +| |-- favicon.png +| |-- i/ +| |-- id.md +| |-- index.md +| |-- keys.txt +| |-- p/ +| |-- pub.key +| |-- rss.xml +| |-- t/ +| `-- talks.md +|-- tmpl/ +| |-- footer.html +| |-- header.html +| |-- meta.html +| |-- rss.xml +| |-- rss_item.xml +| |-- style.css +| `-- template.html +`-- util/ + |-- hdev* + |-- hmdtohtml* + |-- hosakahashi* + `-- minify* + +``` + +Exposed Variables +----------------- + +The following variables are exposed to all running stage executables/scripts and to any generator scripts: + +* `_ORIGIN` - The origin directory that is `pwd` +* `_SITEDIR` - Contains all the site files. In my case I use a bunch of .md files and assets that get built +* `_BUILDROOT` - The `build` directory that contains all the build runs and a symlink (`$_BUILDROOT/latest`) to the last build +* `_BUILDID` - The current running build ID, by default uses UNIX timestamps +* `_BUILDDIR` - The current running build and build ID in absolute path form +* `_STAGEDIR` - Directory containing the stage scripts or executables +* `_SCRIPTDIR` - Directory containing scripts that are used for "out-of-build" site management (for example, `clean.sh` cleans my build dirs) +* `_UTILDIR` - Utilities that are required for building are expected to be built into this directory +* `_PRODDIR` - The current "production" build. The idea is that this can contain the code that is committed to CI and all tests and deployments can be run without pulling in all the `stagnant` repo diff --git a/stages/00-depends.sh b/stages/00-depends.sh index c07e703..dc6678a 100755 --- a/stages/00-depends.sh +++ b/stages/00-depends.sh @@ -1,7 +1,5 @@ #!/bin/sh -e -[ ! -z "$_ROOT" ] || exit 1 -[ -f "$_ROOT/util/minify" ] || exit 2 -[ -f "$_ROOT/util/smu" ] || exit 2 -[ -f "$_ROOT/util/hmdtohtml" ] || exit 2 -[ -f "$_ROOT/util/hosakahashi" ] || exit 2 -[ -d "$_ROOT/site/" ] || exit 2 +[ ! -z "$_UTILDIR" ] || exit 1 +[ -f "$_UTILDIR/minify" ] || command -v minify || exit 2 +[ -f "$_UTILDIR/mdtohtml" ] || command -v mdtohtml || exit 2 +[ -d "$_ORIGIN/site/" ] || exit 2 diff --git a/stages/01-http.sh b/stages/01-http.sh index 9c03bc2..8f274ae 100755 --- a/stages/01-http.sh +++ b/stages/01-http.sh @@ -1,15 +1,17 @@ #!/bin/sh -META="$(cat "$_ROOT"/tmpl/meta.html)" -HEADER="$(cat "$_ROOT"/tmpl/header.html)" -FOOTER="$(cat "$_ROOT"/tmpl/footer.html)" +META="$(cat "$_ORIGIN"/tmpl/meta.html)" +HEADER="$(cat "$_ORIGIN"/tmpl/header.html)" +FOOTER="$(cat "$_ORIGIN"/tmpl/footer.html)" +CSS="$(cat "$_ORIGIN"/tmpl/style.css)" export TMPL_TITLE="⊕ hosaka corp" export TMPL_DESCR="Digital voodoo, cyber-witchcraft, and shell conjuring." export TMPL_STYLE="/style.css" -find "${_BUILD}" -name '*.md' | while IFS= read -r file; do +printf "%s\\n" "$CSS" > "$_SITEDIR/style.css" +find "${_BUILDDIR}" -name '*.md' | while IFS= read -r file; do # Search for template meta tags [ -n "$(grep '//META:style' "$file")" ] && TMPL_STYLE="$(grep '//META:style' "$file" | sed -e 's%//META:style %%')" [ -n "$(grep '//META:title' "$file")" ] && TMPL_TITLE="$(grep '//META:title' "$file" | sed -e 's%//META:title %%')" [ -n "$(grep '//META:description' "$file")" ] && TMPL_DESCR="$(grep '//META:description' "$file" | sed -e 's%//META:description %%')" - BODY="$(grep -v '//META:' "$file" | "$_UTIL"/hmdtohtml | "$_UTIL"/minify --type html)" + BODY="$(grep -v '//META:' "$file" | "$_UTILDIR"/mdtohtml | "$_UTILDIR"/minify --type html)" echo "$META$HEADER$BODY$FOOTER" | sed -e "s/{{title}}/$TMPL_TITLE/g" -e "s/{{description}}/$TMPL_DESCR/g" -e "s%{{style}}%$TMPL_STYLE%g" > "$(echo "$file" | sed 's/\.md/\.html/')" done diff --git a/stages/02-removemd.sh b/stages/02-removemd.sh index 6144026..f774413 100755 --- a/stages/02-removemd.sh +++ b/stages/02-removemd.sh @@ -1,2 +1,2 @@ #!/bin/sh -find "$_BUILD" -iname '*.md' -exec rm {} \; +find "$_BUILDDIR" -iname '*.md' -exec rm {} \; diff --git a/stages/03-minify.sh b/stages/03-minify.sh index bb21a9d..7197a5a 100755 --- a/stages/03-minify.sh +++ b/stages/03-minify.sh @@ -1,3 +1,3 @@ #!/bin/sh -"$_UTIL"/minify --type css < "$_ROOT"/tmpl/style.css > "$_BUILD"/style.css -find "$_BUILD" -type f -name '*.html' -exec sh -c 'cat "$1" | $_UTIL/minify --type html -o "$1"' -- {} \; +"$_UTILDIR"/minify --type css < "$_ROOT"/tmpl/style.css > "$_BUILDDIR"/style.css +find "$_BUILDDIR" -type f -name '*.html' -exec sh -c 'cat "$1" | $_UTILDIR/minify --type html -o "$1"' -- {} \; diff --git a/stagnant.go b/stagnant.go new file mode 100644 index 0000000..b1c04ec --- /dev/null +++ b/stagnant.go @@ -0,0 +1,126 @@ +package main + +import ( + util "./util" + "fmt" + "os" + "os/exec" + "path/filepath" + "sort" + "strconv" + "time" +) + +var builddir = "build" +var stagedir = "stages" +var scriptdir = stagedir + "/scripts" +var utildir = "util" +var proddir = "prod" +var sitedir = "site" +var origin string +var buildid string + +type DirectoryEnv struct { + Path string + Variable string +} + +type StagnantBuild struct { + BuildEnv []DirectoryEnv + BuildRoot, + BuildDir, + StageDir, + ScriptsDir, + ProdDir, + OriginDir, + UtilDir, + SiteDir, + BuildId DirectoryEnv +} + +func main() { + origin, err := os.Getwd() + if err != nil { + fmt.Println(err) + } + var build = StagnantBuild{ + OriginDir: DirectoryEnv{Path: origin, Variable: "_ORIGIN"}, + BuildRoot: DirectoryEnv{Path: origin + "/" + builddir, Variable: "_BUILDROOT"}, + BuildId: DirectoryEnv{Path: strconv.FormatInt(int64(time.Now().Unix()), 10), Variable: "_BUILDID"}, + StageDir: DirectoryEnv{Path: origin + "/" + stagedir, Variable: "_STAGEDIR"}, + ScriptsDir: DirectoryEnv{Path: origin + "/" + scriptdir, Variable: "_SCRIPTDIR"}, + UtilDir: DirectoryEnv{Path: origin + "/" + utildir, Variable: "_UTILDIR"}, + ProdDir: DirectoryEnv{Path: origin + "/" + proddir, Variable: "_PRODDIR"}, + SiteDir: DirectoryEnv{Path: origin + "/" + sitedir, Variable: "_SITEDIR"}, + } + build.BuildDir = DirectoryEnv{Path: build.BuildRoot.Path + "/" + build.BuildId.Path, Variable: "_BUILDDIR"} + //TODO I should make function types for StagnanBuild and create a Add() and Remove() so that these can be tracked instead of this crap + build.BuildEnv = append(build.BuildEnv, build.OriginDir, build.BuildRoot, build.BuildId, build.StageDir, build.ScriptsDir, build.UtilDir, build.ProdDir, build.SiteDir, build.BuildDir) + + if os.MkdirAll(build.BuildRoot.Path, 0755) != nil { + fmt.Println(err) + os.Exit(1) + } + if os.MkdirAll(build.ProdDir.Path, 0755) != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println(build.BuildDir.Path) + //TODO check for required directories + if util.CopyDir(build.SiteDir.Path, build.BuildDir.Path) != nil { + fmt.Println(err) + os.Exit(1) + } + fi, err := os.Lstat(build.BuildRoot.Path + "/latest") + if err != nil && !os.IsNotExist(err) { + fmt.Println(err) + os.Exit(3) + } + if os.IsNotExist(err) { + os.Symlink(build.BuildId.Path, build.BuildRoot.Path+"/latest") + + } else { + switch mode := fi.Mode(); { + case mode&os.ModeSymlink != 0: + err = os.Remove(build.BuildRoot.Path + "/latest") + if err != nil { + os.Exit(2) + } + } + os.Symlink(build.BuildId.Path, build.BuildRoot.Path+"/latest") + } + bds, err := os.Open(build.StageDir.Path) + if err != nil { + //TODO error + os.Exit(3) + } + defer bds.Close() + dirs, err := bds.Readdirnames(0) + if err != nil { + os.Exit(3) + } + //Quick hack, this needs a cleaned up and remove the useless var + var a []string + for _, m := range dirs { + //Ignore filepath error since it is static here + matched, _ := filepath.Match("[0-9]*-*", m) + if matched { + a = append(a, m) + } + } + dirs = a + sort.Strings(dirs) + for _, stage := range dirs { + cmd := exec.Command(build.StageDir.Path + "/" + stage) + cmd.Env = append(os.Environ()) + for _, e := range build.BuildEnv { + //TODO should I be quoting these variables? Almost certainly + cmd.Env = append(cmd.Env, e.Variable+"="+e.Path) + } + fmt.Println(cmd.Path) + if err := cmd.Run(); err != nil { + fmt.Println(err) + os.Exit(4) + } + } +} diff --git a/gen.sh b/stagnant.sh similarity index 60% rename from gen.sh rename to stagnant.sh index 99a8f31..46c526e 100755 --- a/gen.sh +++ b/stagnant.sh @@ -5,16 +5,17 @@ export _ROOT _ORIGIN="$(pwd)" _ROOT=$(dirname "$(readlink -f "$0")") -export _FUNCDIR="$_ROOT/stages" +export _STAGEDIR="$_ROOT/stages" export _SCRIPTDIR="$_ROOT/stages/scripts" export _BUILDROOT="$_ROOT/build" -export _UTIL="$_ROOT/util" -export _PROD="$_ROOT/prod" -export _ID="" -export _BUILD="" +export _UTILDIR="$_ROOT/util" +export _PRODDIR="$_ROOT/prod" +export _SITEDIR="$_ROOT/site" +export _BUILDID="" +export _BUILDDIR="" export _DEPLOY=0 -_ID="$(date +%s)" -_BUILD="$_BUILDROOT/$_ID" +_BUILDID="$(date +%s)" +_BUILDDIR="$_BUILDROOT/$_BUILDID" HELPDIAG="[-p]\\n -p: tag and stage for deployment\\n -s [script]: list scripts with no arguments, or run a script" @@ -57,24 +58,22 @@ while :; do done cd "$_ROOT" || exit 3 -mkdir -p "$_BUILD" -printf "%s\\n" "$_ID" -cp -r "${_ROOT}"/site/* "$_BUILD" +mkdir -p "$_BUILDDIR" +printf "%s\\n" "$_BUILDDIR" +cp -r "${_ROOT}"/site/* "$_BUILDDIR" [ -h "$_BUILDROOT/latest" ] && rm "$_BUILDROOT/latest" -ln -s "$_BUILD" "$_BUILDROOT/latest" -cd "$_BUILD" || exit 3 -find "$_FUNCDIR/" -type f -name '[0-9]*-*.sh' -print0 | sort -n -z | xargs -0 -r sh -c '"$@" || exit 255' +ln -s "$_BUILDDIR" "$_BUILDROOT/latest" +cd "$_BUILDDIR" || exit 3 -# This is the old order rendering function that was based around staged execution. In reality just making the "stages" numeric makes more sense -#export _ORDER="pre:render:post" -#for f in $(printf "%s\\n" "$_ORDER" | tr ':' '\n'); do -# find "$_FUNCDIR/$f" -type f -print0 -name '[0-9]*-*.sh' | xargs -0 -I {} sh -c "{}" -#done +# we should prefer a single find and args pipeline with null termination... but unfortunately the xargs sh -c "{}" doesn't seem to exit properly +for sc in $(find "$_STAGEDIR/" -type f -name '[0-9]*-*.sh' -print | sort -n); do + $sc || (printf "%s stage failed\\n" "$(basename "$sc")" >&2 ; exit 2) +done if [ "$_DEPLOY" -ne 0 ]; then - [ -d "$_PROD" ] && rm -rf "$_PROD" - mkdir -p "$_PROD" - cp -r "$_BUILDROOT/latest/"* "$_PROD" + [ -d "$_PRODDIR" ] && rm -rf "$_PRODDIR" + mkdir -p "$_PRODDIDIRR" + cp -r "$_BUILDROOT/latest/"* "$_PRODDIR" fi cd "$_ORIGIN" || exit 3 diff --git a/tmpl/footer.html b/tmpl/footer.html new file mode 100644 index 0000000..8f36789 --- /dev/null +++ b/tmpl/footer.html @@ -0,0 +1,5 @@ + + + diff --git a/tmpl/header.html b/tmpl/header.html new file mode 100644 index 0000000..4f58e98 --- /dev/null +++ b/tmpl/header.html @@ -0,0 +1,8 @@ +


a hosaka corp project

+
+ + diff --git a/tmpl/meta.html b/tmpl/meta.html new file mode 100644 index 0000000..74c4046 --- /dev/null +++ b/tmpl/meta.html @@ -0,0 +1,10 @@ + + + + + + + + + +{{title}} diff --git a/tmpl/style.css b/tmpl/style.css new file mode 100644 index 0000000..5c2ac50 --- /dev/null +++ b/tmpl/style.css @@ -0,0 +1,238 @@ +body { + padding: 0; + margin: 0; + font-family: Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Courier New, monospace, serif; + font-size: 100%; + background-color: #222; + overflow-y: scroll; +} + +table { + border-collapse: collapse; +} + +tr, td { + padding: 10px; + border: 1px #93a1a1 solid; + text-align: center; +} +th { + color: #222; + background-color: #93a1a1; +} + +#wrapper { + width: 90%; + max-width: 720px; + margin: auto; +} + + +#post { + padding: 5px; +} + +#post:hover { + background-color: #111; +} + +#rsslink { + float: right; +} + +footer { + text-align: center; + margin: auto; + width: 70%; + max-width: 720px; + border-top: 1px solid #333; + padding: 10px; +} + +#blank { + height: 100%; + padding: 10px; +} + +h1 { + font-size: 40px; +} + +h1,h2 { + display: block; +} + +h2 { + font-weight: 400; + color: #666; + font-size: 100%; +} + +h3 { + text-decoration: underline; +} + +a { + color: #666; + text-decoration: none; + border-bottom: 1px dotted grey; +} + +a:hover { + color: #cee318; +} + +h1 a,h2 a,h3 a { + font-style: normal; +} + +header { + text-align: center; +} + +header h1 a { + color: #93a1a1; + border: 0; + text-shadow: 0 0 8px #000; +} + +header h1 a:hover { + color: #fff; + text-shadow: 0 0 8px #cee318; +} + +code,pre { + color: inherit; + font-family: monospace; + font-size: 95%; + padding: 2px; + background-color: #93a1a1; + color: #222; + border-radius: 4px; + word-wrap: break-word; + scrollbar-color: #cee318 #1a1a1a; + scrollbar-width: thin; +} + +pre code:before,pre code:after { + content: none; +} + +pre { + padding: 10px; + overflow-x: auto; + overflow-y: hidden; +} + +pre code { + padding: 0; + border: 0; +} + +blockquote,q { + color: #666; + font-style: italic; + border-left: 8px solid #aaa; + padding-left: 8px; + margin-left: -8px; +} + +.strike { + text-decoration: line-through; +} + +img { + display: block; + padding: 8px; + border-radius: 4px; + margin: auto; + max-width: 100%; +} + +#logo { + display: block; + padding: 8px; + border-radius: 4px; + margin: auto; + max-width: 5em; + max-height: 5em; +} + +video { + width: 100%; +} + +.caption { + display: block; + font-style: italic; + margin: auto; + text-align: center; +} + +html { + font-family: monospace; + color: #aaa; + text-shadow: 0 0 1px rgba(0,0,0,.3); + scroll-behavior: smooth; +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: outside none none; +} + +.nav::after { + display: table; + content: " "; + clear: both; +} + +.nav>li { + position: relative; + display: block; +} + +.nav>li>a { + position: relative; + display: block; + padding: 10px 15px; + border-bottom: 1px solid #333; +} + +.nav>li>a:focus,.nav>li>a:hover { + text-decoration: none; + background-color: #171717; + border-bottom: 1px solid #cee318; +} + +.nav-tabs { + border-bottom: 1px solid #333; +} + +.nav-tabs>li { + float: left; + margin-bottom: -1px; +} + +.nav-tabs>li>a { + margin-right: 2px; + line-height: 1.42857; +} + +.nav-tabs>li.active>a { + cursor: default; + border-bottom: 1px solid #cee318; +} + + +::-webkit-scrollbar { + width: 5px; + height: 8px; + background-color: #1a1a1a; +} + +::-webkit-scrollbar-thumb { + background: #cee318; +} + diff --git a/util/file.go b/util/file.go new file mode 100644 index 0000000..a5dec68 --- /dev/null +++ b/util/file.go @@ -0,0 +1,134 @@ +package util + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +/* MIT License + * + * Copyright (c) 2017 Roland Singer [roland.singer@desertbit.com] + * + * 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. + */ + +// CopyFile copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. The file mode will be copied from the source and +// the copied data is synced/flushed to stable storage. +func CopyFile(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + if e := out.Close(); e != nil { + err = e + } + }() + + _, err = io.Copy(out, in) + if err != nil { + return + } + + err = out.Sync() + if err != nil { + return + } + + si, err := os.Stat(src) + if err != nil { + return + } + err = os.Chmod(dst, si.Mode()) + if err != nil { + return + } + + return +} + +// CopyDir recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = os.MkdirAll(dst, si.Mode()) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +} -- 2.45.2