From 9198625280d15688e3d392bd30a611cc92c83313 Mon Sep 17 00:00:00 2001
From: Tue Herlau <tuhe@dtu.dk>
Date: Mon, 13 Sep 2021 15:32:36 +0200
Subject: [PATCH] Snipper compatibility work with 02465

---
 .gitignore                                    | 138 ++++++++++
 setup.py                                      |   2 +-
 src/snipper/__init__.py                       |   2 +
 .../__pycache__/__init__.cpython-38.pyc       | Bin 167 -> 221 bytes
 .../__pycache__/block_parsing.cpython-38.pyc  | Bin 3161 -> 3482 bytes
 src/snipper/__pycache__/fix_bf.cpython-38.pyc | Bin 2198 -> 2505 bytes
 .../__pycache__/fix_cite.cpython-38.pyc       | Bin 2536 -> 2505 bytes
 src/snipper/__pycache__/fix_i.cpython-38.pyc  | Bin 2820 -> 2820 bytes
 src/snipper/__pycache__/fix_s.cpython-38.pyc  | Bin 1384 -> 1571 bytes
 .../__pycache__/load_citations.cpython-38.pyc | Bin 4979 -> 2652 bytes
 .../__pycache__/snip_dir.cpython-38.pyc       | Bin 2103 -> 2057 bytes
 .../__pycache__/snipper_main.cpython-38.pyc   | Bin 2644 -> 3025 bytes
 src/snipper/block_parsing.py                  |  30 ++-
 src/snipper/fix_bf.py                         |  63 +++--
 src/snipper/fix_cite.py                       |   2 +-
 src/snipper/fix_i.py                          |  18 +-
 src/snipper/fix_s.py                          |  25 +-
 src/snipper/load_citations.py                 | 237 +++++++++---------
 src/snipper/snip_dir.py                       |  42 +++-
 src/snipper/snipper_main.py                   |  17 +-
 20 files changed, 384 insertions(+), 192 deletions(-)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5391d87
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,138 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
\ No newline at end of file
diff --git a/setup.py b/setup.py
index ad71618..a7fc926 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
 
 setuptools.setup(
     name="codesnipper",
-    version="0.1.0",
+    version="0.1.1",
     author="Tue Herlau",
     author_email="tuhe@dtu.dk",
     description="A lightweight framework for censoring student solutions files and extracting code + output",
diff --git a/src/snipper/__init__.py b/src/snipper/__init__.py
index f102a9c..4e820bd 100644
--- a/src/snipper/__init__.py
+++ b/src/snipper/__init__.py
@@ -1 +1,3 @@
 __version__ = "0.0.1"
+from snipper.snip_dir import snip_dir
+
diff --git a/src/snipper/__pycache__/__init__.cpython-38.pyc b/src/snipper/__pycache__/__init__.cpython-38.pyc
index f52a70bcdae56e64584b49d2330bcdd216e21e6d..eae9fb89b94899b9aab163c73358f43efa116212 100644
GIT binary patch
delta 157
zcmZ3^c$cw0l$V!_0SF{sSSC6E>Bk@rGGGF79Dul31W2SXL@}f=rZD9&<}yVwF*2ku
z2Qz50RIwW98R!|l1S(+AWW2>uoR?V;pORVRr^y_}9UotoT2!2wpBEn=B><8sNG;NX
bsVL$F8NZUDh#5!~F;9%}VlM`XurLAu*oPp2

delta 102
zcmcc1xSTN}l$V!_0SK;sHcS);(vLwLWWWgIH~?`m2arf%h+;@#3}(<|s$w<JGte{i
k(`3BG9UotoT2!2wpBEp$lA(wRsIG`<VviT=XCR9K0CtEIL;wH)

diff --git a/src/snipper/__pycache__/block_parsing.cpython-38.pyc b/src/snipper/__pycache__/block_parsing.cpython-38.pyc
index 5f0fa38e06964f4ccf08c26117b7b787eb3b0791..f863b8116bd2e1ce5093ddf2a92c11e93ae21392 100644
GIT binary patch
delta 1887
zcmah~UvC>l5Z~F`^ZoJplGsUIH>F8QDLNtwO{2clv{hPoBM1c~6cJgDH*t)OZT3zA
zp*@U9Py=eEMkijVkmAQm2&tcdcO)bpc!2u``3iY~nRA*yNEK(@+|2Ck>~Cgg#($sx
zaoYc|P;ePOzi!>Czw7V$7v1|PNw&)5#NOiN8CT|a*s3XuvLO8(Sk1{XIgT1jdMhj_
zJ?oVC0La17!|^Ndy@zkOgG4h46&6FpG;D+9ZLStINTWh4qQc5dnq}g1#5W;^ZuQt!
zsQy{oCZNov^(`lB<gE%DoNO-RK`7bi95Sa4nITYjIW^Ziy+-?hz$zlC+3$tX^+T8)
zaSI3k;FFr0Efq$o=+wjLDJTOi1FpttgR$Oet32(`h(|hiy)!_!wNHAjZW$FJX&Kny
z!7RRSg1U%ugVOT+8AB$AFS!mqT-gPQNurRxV=WeQ3?Fsd{9_&)v3LNR6tb~n$EG$U
zYuoCk#F(_HnWi)jgk-dN8{(YKQBI|38)|D8o?vcAnkVchqsw<8wzL)7bbz+Cp@qz0
z{;fv>k8)yHJLsOPpV99BZ2pWu+WVX!3`WNf@p^$phI9z~H-PS)Hh&Ci32y|=2;vAa
zO74lI-O>IgryD~E1Sty$Z!;eWoyYvRk9h29Pv$FZ-yAYLC#St_K#f-&Fpaf%>JTmq
z)&IECT>4e^fMGIuR>5{$(1j5kEhyD!F@d|dEFz^9EFJA0t(3BrNu*iIF#tgUU^Qm(
zBVz54ViW;hz)^%vtissWKDy0U2{T8`95FMEyquyhT^RD`kmoD76RWknd_V=$INexO
zE!0>gs+zBmXts}4GlV$#%bXwV8&V+8r73ee=;=O{12)QODH75?k+XG2^R}hFl=zj-
z2m$trnnM3slPEgw5N@4;^1R1a2+AaqCcCWJnuomy`)tVe_-A5e)Fc6<W*Dhf|7r4l
zZVu*?Uvl>bFLp+~(!|KtCXWXOYYQbbjC=+#LUjS{)GU#+?WpEZcMac=8Z5^+^Uh|G
zpbtzW8FsSFj=LIT3u<WR)CCGbvLSSR<Y375`4H2*$&Uc=LwM_hz`&^bomP~Zo!;jg
zO3k377vgyX7el%_!jrOK+EgTEqh+-76%<n;Ta73wS~n}N(gvD1jJvP^zTrcW+hFr#
z&AO7ORdt0(DS2pJ8-I=1A`zd+`Q$gNf2l-lHtNV62iWn6fvJMzBfARkCqLLX2CtJL
z-C-)~s=1)f5$lIhT}8F1-mIZAOhvc;=|*6w0xg<-)#~Daq=}b0Z-3F)=tr$ySJ7BY
zjef76O}t=CQJNLqLpD8?MVw7!p2!U(t4{KcGq-r1<Y^+=ZBo7zjnT~IEHdE|5(b_j
zz~|nW4MHSCXIPN5Mpw!}-AUeazlN8SKio@GTcpqa*0MoN!&ogPM*bnpB#-m6a65UH
zFPnGKs-OIxf9W=Hn4{yrh5(iWJGE*x*=y8lS=ed<ZL-KRRO$|i1WVmb=De##%DpgM
M+jj7G-xjX@FPvy%vH$=8

delta 1542
zcmah}OK%)S5bo-E?Ck8!dW|3J#j#}*5i}wg2oC|?_&^|$0Qs_lg9WVC?s&D>c$c0z
zL{X0xVMlQ!B4LfV5FzZJz(3&1<HU{O58xAbqiFeRHdcTOBXv!6J-@21?r-LQo2$Mb
zhQ7cz_sc7thrv<xg5ShWi4CD9j$U4_*HlFX2ejd+P*o4ahO7LRh$g>JrsPnv<D;*J
zzl&vI#Y@l}k`jvU$cDHllvLJ%NM%o?Rw4{^`|?AX+NpDwwn^E_Iq*`~*h=hpdQFLz
zNXQ6dH^e@XFz%Q)juISipVE>kDR<Zq%H6TFIiv?d6{3cCAilNs<RPV=@zOFUGi77L
z)vF6tzHcL~pH_^IR8s!{yZ^KKBO&D<ONKB`e?kl@vDC1_b$mex@9)U3NPl3^IAb%$
zF=Fg0k9Vdr4$zf#*82sdqN2J;gTyugdKTQ5X=p+fHpH=WB2c|F;XXl9Z3+{%;CY^7
zXe>1W;lDybR%?p80*#ewQaypjR!tQ)Xr^(v?XZ}Fm!+mbS>r=fd8C;sG_#<E2U0m)
zK({y!25fxZ7X@60<9UT$LJbpU0%1cqBMC5D1?Cd|b;xjHNMxxw@Ky_+n4`w}smvxT
z(?c6$)8f&24+JF=w@@LIvI-99>p;PI3#p^ra~#E<Lt_U1VdXK86y^2Y+3x8$$(_yJ
z{??8rWXPRCKTcjeW&U(D8VyHd$(7vR?Zk<$Kpv6ip)U9_3E0#Aq~F_{2NxIjhAz|e
zNRm6c-6+Dh<H2q(S!p6wXE$D3Ve2hUbN=xKaj>Sa;%yKkjz#vD^TM@MV(R5SDT&d1
z2e-oR5pIH|Q0xJEdTguG3B85+#|@B1qBw8;jFyUu^ejT<*>|P+h`BDpepF4#&@noF
zLPzp58}@WYS0l=u0%Vl^>|UfO`_sL)d`7F0qxlpx1B6AQdxP9oy{$wqLXy4fy;@%e
z6LS}Y^y#t;$dhw)dpUdPE(iJo#Cn0rZ1$tKQoF?LG?OZmsjO5UEHbt+UN(>9<CIAM
zboO<*NjI`z%P$QhHk<*;?T+qV(R0jJ<D{dLcGBs#A&hf-uk-26$kib$oq_J{CHhI`
z%WvP>x;aRC{XIRwduz}iL^aKQ*ZfKqR^#I>@z!JV43p<UHp;E+j{nqa&w<aa&E1{x
z@juIYuD*!Lfa2>Qf&%GTHdSR%@yM2e%zpPz!VT6JFC^*J>|W(_TFU%japq%|k4MKN
zbP!9gWmkhQ>0<U%u(a?7^tn{G{xf%)pXKeW6fV5Z96HBCRgj3*qf%aKw^e_u-7cD;
eCt1m*>s2OeOq$tdc&Ww(*pBaczW1hQ``+IYdN~RJ

diff --git a/src/snipper/__pycache__/fix_bf.cpython-38.pyc b/src/snipper/__pycache__/fix_bf.cpython-38.pyc
index 50edfabfa447d901387da5638cca83d0bdb560f9..08110df11cf8ba047f40b6a36f84bec56cdd98a5 100644
GIT binary patch
delta 1296
zcmZWo&5ImG6tDNHy1J)lcHFqht`nCWg3&a{5cd+)adR-N5g}O!UWV0Swri$or>AGO
zx>r<c7%(e%@B?j7@B=nSFM<azf;TUMe?k2R-g5P%uX?hHXf^ffy{cEQfAxFseRb~l
zjrN;sYayfYOY-vYkMN83_Hv&r#S~<$?^euUihTs9^NdeLU(~uF-RpZ(*_UK{N}jO3
zuUaZljT7hxYE6ZzNm@g}T^6<fbU(Qrf%TGjHZ4ezX|6_j8KG?+X0v!QDCSvO+Ge=2
z-kGKOPhY-{7aDYtoUnV%Li__*OqFg_4{#4IRoD63SL<_%Ta-GTN2uL%EM&Sxt9FOF
z?Zgw+ul!bf-5tu|d_KyR=GBI{1Lvw(^fqh9`ik?m-Z*`4F!rmT#fvv!6V_>NLWm)R
zpr0X!z1SmpHr!=N^HrL~0}>@O#9)WrgN0Xp<K0<GgQs|(E&LLUe-D(Hc=}`GJ0Imu
zu+~xWkFY~N25Mh0T%RhLfRX3fBFJwUp?^$BQ?QWvOB4wx5=?~t&3Fd3SOPgF%y`N-
z@>`~Y@A;Oq8pgjuX6VcWw<v%Kj2t7SrFmNe!PUMK8`bA>|3btych-3)99>aEZ&!cG
z?Hgh;8qKXN(u~rk*XYP%99tZXEM_|^svq%f!@(+#<Kz5!axE@b2YzQMK!C(&q5ZcM
zx=Z^pk~kX<#v>j-A%k9v^jco?N&I|@3t@y}#Mq^y50y~f2_su}*eTp+H*3BP(vII@
z*XeHgP#**B1s%4er9qU3-yspxG?2pA{4+SH-Fj>xOKt=ujTvqv1>i@4aXOAUvDJFW
z){}&c3YeF}BzE+j4ab|Hd6#B!nM6uu@YclxTmV5~l&&QZcA<p<gB2bL3JP@wXmNxS
z`K$7sSa|n;jP_I-m()G;-8bAH0uqwG7DM*IMOUzgJKY|&(l9G_yN{i1yC{dc91z^)
zV5pPAdUa~7AmfEv?oi)6N8$8UCn6FNp`^unX*rq}b*i*0`N-GXXFgWO$K~v}w2)gk
ztOFGmRsLV#a)s?CW3RDdlWsGr0)Z_E0I987aE-K(>sMTS+}n#_FA8mgDkm;yv#g*8
z<SDhR?q;K87{9Nv+6|uGuglbdtbx^Sxx(q<{0qb5nhbi=nL5r!yDn>m>-K>D-hy==
ITnV502Ph#FssI20

delta 1053
zcmY*Y&1+Oi5bx^l_cfV}quEW2Q4kdL3C1Lgh#~7CNbr;_EFK)h@O=Gd80XCg^<LQQ
z(!(5#=*7eGU@wcHM~`0o3%rPbfY1ng-s}DaTlL}xc2nI|)m7D1UB7<6@Yl)K-TC>3
zpz-P5^<+Exvvqd1Ns<u;a?#;k<}j2UIaD15EUR1(y^cq`Hr|ow_$D%e36G!?82DAh
zjej}ZZY-VGOUJ{)%|Fb?>0p0?<1{OZ7@Xf9nQT<1k_|tP7VRF95^p|i$R9&uDS}*G
zbCA9UC;JA&>Xh1nbJacd;BtKq&MQjnPhyD?**nN_j&|fjoad!g{iE)*PVk|hOeWdL
zpsud#J!n@?^wwf+Si7*b^Sb@R{jOhq)Hi2x5!z}I7NMaV&;YzZ20Pzn9FT@d5f!&-
zF<@jLrqYB@MHh@R`Y4*JQdnWU5*|sbtTg^}X@ckKzMkr-N7J{Ow6H9!-&OQ_(XN9L
z!c~fw@_T$4+#{c8g{b(CmO3u^J%*`Y?Rk5%z{;2Mm6(Pl*zhqJJ@D|E4LP>)2D0X*
zQBUMD*;uG;;hDgf#_xd*eh|}Wv`M}|zA(`E*7_EfL=Q%?N7<Rc27e0^zEDdnMK&a<
z!BS5yk?$p|&73Ewkl+Tr$(FE@4Z4y_YVIOX!pQ04scPH*Wri1tj}>ZGE%3rUiW|t~
zxw)cDu-wlMOH5ouP5aC$(sIj_vT*+3w*-3yHrYX@sgqgeWWGUI(St0Td;@`tj`7z$
z7;TVoaYl%l5YU8+G((!Fi4Q?TQ*O#O{ISd^c(}2;MW{{k;@0X{cRa~S0$$-wI#nh;
zT(4ETR14$4MRZGAj>mc7f(fPDPx3gZtHL!puU&CHVfGx`!@l#=gK~UO9w&T_Piipw
z$7!%v!8jF^W2=20@CNZQ?8G7dM4W3Cqdp-USM#iwq`xD7Dpb{jXyxbnbs4y|&+!E{
b<iCCp7j6y5<{-~*afj-ALTE!%g;)LuTO{4T

diff --git a/src/snipper/__pycache__/fix_cite.cpython-38.pyc b/src/snipper/__pycache__/fix_cite.cpython-38.pyc
index 8182783048657d4e03b7fdc09bc39298fccc3065..41ff88198ed4802e312744218929d4693baa9cdc 100644
GIT binary patch
delta 229
zcmaDMd{US<l$V!_0SIDWTPD8T$h(_aPXx#-VW?rKVN796VM<}{WvOMZVGd@{WbvzF
zi784=3n`KTD$!&tk_S?gMOgGXSbz)$MggYHzARac%(oc1ChuW&VvL&npLG-4EtahO
z%)H6l*vfcRfvRq?6qQtz6lrg^U^ixDRGOT_p>3cE6nE2Pyv327np%*Wmr|_BR-_9Q
zD`El>dLUh_1x1;8B}E1x0prO>Ipmd1Kul8*VFn`1L4*a6h+;}DD$)k=tR}N_J_G=D
CKQ$Ww

delta 260
zcmX>p{6d&Fl$V!_0SHzfGEIEGk#{$<j}nkq!cfCd!<fRD!j!_?%Tmi+!kEIcfT@Og
zA!9QmBSQ^yFoPzmUlmJCQEFO9kql77Ek-U)<|26zi?yIAGq2<pOIChn-sEr0`dn;4
zQ3gf<Mn1;Px-40YtVN1I{^VJ#PK>uE-(}szTBHo(Ph~6PQ3o<^u@se5loaW07H2nR
zWK@~#&7rNQ0~B}DWW2?Zotj#ZnwL_n$p+R`!~~QmG5`@ER~H$92-C^SIOLVhKumKG
aVF4m6L4*~Mh+;}DD$)b-Y$m_pcnARNjyuZ$

diff --git a/src/snipper/__pycache__/fix_i.cpython-38.pyc b/src/snipper/__pycache__/fix_i.cpython-38.pyc
index b538f528512b367e6f1e99eaf64f3f4e0c4363b5..1f1675e90766d6fb6d166d74c20670272061e74b 100644
GIT binary patch
delta 931
zcmYjPyH6BB7@u$UakGy*z!(rcjU<8`AmIXyc%X^ISQuZ#Xf7e=!Yn6n?47gAqu2|D
zm|$T+rnSI=0t*wPrGLqEloS@WQs_5(M4e=Q^S!@selxqhyS;^H`MgVTojjNhzUTJ~
zMR>74I#wne8g|tV?bRGN*Ql7|+QO<%#4^dPI^5#+E~$DP=1D2H&j(FKqvXpw4uvi(
zXycy89aDJ<%R74sTXi_&4tKft-e|k2d*E`v4j%zX>ZUo)Dnw&HX?r}+3%jK4rzTpu
z`Uxj*DWX+}J~BrZitT*r9e8Oz%k>U??sL0Nwf6dR>Ze{x-q3bI=USwnBqTMZY4j8e
zZIY6drKC=mDGuHJPykM-peC3s8y8FqwXaW%pKwKUa_{6ft}NsiID(-`=$i>ozO)Vp
zmA4Yrcr&T3H+iVct)}3jh;EfbqqZFpQIaF}0)X7JFIzASHkF_3vYo@IQeGb0vpZX%
zvf@^-6)8L1ZiP#UxTrf^(h*%^K$r94BoIk0+)hNGoG{{NqJdUrz6t}b%p_Q82+g>%
zI&o%mBUx4uD;h-NDmuidhHDxIHIy=#(l~;koW(|SX|1-r5fxC4bs)is8MxrXIqE|<
zo{TbJL-I5?-lv<1n+PSUj3lUw8EvrS9X2zpYadZX$0rnQo^{gRPNPa3CQHrr*6d?>
z$gYEnDd*)eyEUmf7d;3i5Mx@tjc^2FTFcqISpm1wScy9op2|l~&CVW%$#2f2ou%E7
zKb(mte+SGaQxGUvRInr*tfq{$%~%=8kpg5X>{P$2`(!OFu@Ti)eAMG11Of$cD>8Bi
z^3PE#`QknvVqAW3rxp}##>$L>^-$%u&fc($Zk21DX*%aW7XICYRq&f^G{RZ^3*&AC
Oq!(#MqLFr&GUE?@-P;@h

delta 916
zcmZWnO-~a+7@l`_yI<XHiw6j#8i@iekQD=%7%N`T#ES-riP?nIrL!%Cvc=gg5V9Lg
z#Dv6n!1)7^=mFz}_!qo+lH=Swdi2UgXBLAdPBPCt@3ZgMygPgSd;Nu{j$<LQzI~nb
zkBSF{GRz!IOgbvb?IBMiu!ySK!T!j}34ubN@YBRz6gwJa*Kl+969IxcMrtM2s!7Z>
zOkZY-mF7buWBD|n<X3HC5R2rA{Z>is#6EP0(}E)al-P+*j2e<Sh*F0XNO2FPg+vuf
zX%G?g8Vk&|U;xddrE*$KoWnv=%wnDLVUajQYhh`=I86$Plb}~PEy>__%3!UOq68&I
zf?BwV#a;RyQ4rm8s8d~>JDMA<1f32C>r8Q7cL%#JRiy(C<IfAqPq<<V>%os-Vr7wM
zU}llDD0FJE=eZ9fUcMdHNjI*qcS*q2tu7_BEIxa>5T$mE=olN<mOx?q+T|RsfTpvL
z+D%PPvsz-`wYi~g#MRJW4?KM<*xm>lv8QjG6E>_5?e@`OnK2*5K8@?ac1(TG3_|jU
zDnjMzO5hW&#(uj)bHbbFGC!_%dvTLPL~qCxcq`!859w85(5i&%5=JD9T|h-JZZ3Da
zjkS8S7gpsMqDcV=Dqw&GHeAAX#w=_=N!W%s4VRtj?trXLX9ZL-S7N_KZ%aXC1IFEo
ztZ|GvcBeRO{#UUU1#zRhzA^WVCB_(-?7i`LS{A4B^;Ln6OMXY@0D4REtmmv<tJY~p
z=g}S;HS3!E#Z+f+%xNtPn_yqesfCN?vaUGz9F{pWs3E*PWo<L!iWnFNF|y!1`#qT_
zE0LqUu-=w=qfr3}3Sd2C_pM>)iLk0Vofik4WRW#9L#6c31^+f`)Woakb%Hth79&|#
KOioPDR(=CaAkwb@

diff --git a/src/snipper/__pycache__/fix_s.cpython-38.pyc b/src/snipper/__pycache__/fix_s.cpython-38.pyc
index 82d5e8e8b679eff28ef31891521b5b451b77d499..9ee4629819d4fb80d652bde8b91cbd64e3ec86af 100644
GIT binary patch
literal 1571
zcmZ8h&yO256t+D-Gn=I8vSqufs8T9X(!)Ya3)?DPRqBdUREbs<)hJ~&J9g4(CNs0P
z(}l=61vvDAxFI2=<jB9lpW!Q~{tJuH@;u3I0ZX=@JumOQ?|q;1tkDP%4EwKp@kbs)
zf11O;hXaR)FvS~KD55w+V;o{^Y?6_Xz;0#M*beRL&d!{%8@gaS)cqcX9`&dXyH9Ij
zjRiCq5&k3dSZ%SfL~|V0S$)x1q675e<30(S;RV*B8>~4e7=cw^Tx1uPcn<mC)G+%|
zpOUAj(|il{pbpj>lnvrZCTW_;$8c^OW<@fJQptEM8C(Q|NtQ)I^0Z8_`O$g+T4c~Y
zf+-X%5=qE3!;&b#IVVul-i#<h$qrgr)S~v1h&^e;jk5!_o4t#2q>Sb6An<Y&S(+L*
zQ#7*_Ubq#qt9>%FXU?h<$~l1F&RpdxOW9p?^jukhr#8LPe!}F0=WUUvrDzX$F>c?;
z(wvDK?WuMBn$S%lV=f~Z52Kh5Md*(hE8!%*)J}z3+j%@@|3PU1FdODmgYqgYL!{7)
z_h(pP3KauJ-(dXNI%*x+PzUBNIQ@<~mL|KOYMf}CaUZU#<>bzz`~7EvanYBPm#lwK
zB$F}ArRbZYOUC<xC+GGc{WcQ2<##&hW~q=#F)sHnnL1277ZDV|obC*n1h+2G7H47~
zj5FVk#|e09+uU%2L|fp@4Ib^rkT0j&0j9^I<7ubrRp?fo5ZuUGyTwG76UhzNnj}T&
zipuL9B3?J2oT>(cTx`LDh>x43g*TloV&fKWIsxXU^28KzHVg`rr4rB|U`S{!P-{u7
ztU0E5Wb>Pnys{Ry!b>c`27!Quk+Rm(c(alQ2nda8{2N6U9wpRTk{O{Ov|rJ0WQdis
zjX*xm!dK1`q$6uUp0$mt*nyE8JVN&ekSW|@Wv_GpLOo?~qnwzP`Y>yA@>6T(DQ~f%
zTvNx_=q~ySJweaW(QOJE=>(6tc_Syde}X@mlIv}agSUU5o}OwPO)bMEKJL`3UT7<0
zxh93sb{Wf;K!l(v*UoqZ8@~uqw8vSQ#R=2)t0K*{T>xX+Ka2%4I@QiG2l6^T-!#s_
zimS*3sSVPMMJ8<MB}FblVikuW$uez$qsD{(bLfirHH*YGh_xaS`{XhXfJPtJfy973
z#QY|FopyuXt)O?i4R7i}9P~b`jKiRJC+OX62ffc>esT734-WU%4#!YNp#3Ns$7vo#
zQ^%mV&o7&p!GrVB+0+|Jk!38AX_1Rc!~6pS@zVO0?5_T!%9x8ZAFh<Wb@pw&S4k?V
zSKY2E*F@Yh`Ayy^(t#+mRIUX59>iZY9gj4At(}sm(6kTD_4)H%S(W1b?zo_njO`n`
Xg@I77`cx<W6~ATIeXHsjZsLCcStWu?

literal 1384
zcmZ8h&5j#I5bo;v@px=+oUmlIAR?nkY<yV=WCKLbDpFRQtOyb%ppnsNcDlWpcz*2e
zb{4F0gye8WoDq_J1m1!N-~sx|DX$O-E!AW1^3zgRSGUVmU)A^7k6SID;QH<NgXj;N
zkU!1k-^;<vJ>2pI8b%nTB!d9J_%x*f#nVcy%nodPvzhaf1P*gq4NsSOL5+K?KB8jA
z9bQ}3S7ZT!&;4a%MUKeVHwH9l1}(nDeBNA8Am}xgmw0Oh3#@^+ra32{Gx~^hTdxoU
z>4I)BK8&WRVo9tX;JtO67V#)d6c>@=F_^H;a`D8z$bH=M2^vKd=AMJ1C3dr*h~ny;
zmQYegcgZeUS|weXnpYAsw@O&r71Fl`bZ*ZbW-<FEDV-ziqqIu9M<%yR3$Zz~w>uBH
znu@$5^JFYLLs4X%y)?<W-0RG&>+ec!=TY`wbk+vlBZKZ5niEnQJWv8dEQrlRZ1hKK
z(w^8@1+-JO`U~k=n(lv~A=Z!z(`02{-@1Qi@K|yo2WtA14~~j>n(<u8fdMt<Vjx9)
z;SZB<Lb*TwR{Om)kt!~-@nOp#@zA)05+C<$_k=49>*0u_R34&p9@}*U#Yk<N7h!tQ
zZiH2GrXA!WlU-Lk)daL#!Ir{g)^0IX<Eavc7fs{hAN<0IQ9D&dqdd8VhENZ-V4K>|
zhHVgU;7e?ZoiCHqe6OPTS9BB(5h=D5Jflb_tUz8<NTl(Al-AOzD1EV}6p1IJy7-Kt
z1b!kv(-SD29fDNTrCT~Hn7gWmbXYqm9L(n#d7$X+?@=!JRxj-ZFlS_o@0eTKJ0z#Z
zvKnr0L4Rn^Yh`Wem#(S(B{?8pkcZ?eGI@^?B((luMz41?_^;vpSAU(Ioe3je4dKi(
z#K38{Q8lb-f%+Lo>M4#^vP5Vn8{r|$fVBOrNOEl#IC||JN0J+%X)DF6b1IOEt|u;;
zKz}{?5WgAJhe^uADrGG$a)mP4Fp{R)!axnf|1;yt=s6E%8*|AkXb5=F1Rsg<aQko%
z#2&uRdVc?=-~X_Ks=pig{f{c=*ze!+`v)Dre;fD5=O2E8mrpkVrwGE=UKnOkl852U
zF^$|2+a{-f_hNP1x)B#?%43xjxvW$ub}=Y;bEEV9^*_Qm5;Dn8Ds5JCtl()XZ<-<|
zc^GP6j?+X{Iu(~O$$F+R((qh6W0Bxo-Z9S?zp}MgN!9CR1)HY)(9kBYqG_6uP_N-N
M?WSintFdgs-!xoIpa1{>

diff --git a/src/snipper/__pycache__/load_citations.cpython-38.pyc b/src/snipper/__pycache__/load_citations.cpython-38.pyc
index e79c0857a12b2136f7aa6dcb235a3dc9c4293688..d56371f0185a8d8fca7bfd94be7ecc44f1d2f380 100644
GIT binary patch
delta 1403
zcmZ8h&2Jk;6rb5|+v|;+22!X|Ow~4BQ4v%LC}3I8f<!^8N-9*eO=Yb;<769q?aa=S
zWHT!tTF$820|!8gkBAdO`~jTcZy3QDg)0Z7ND$tv!-v?_{^relzj^P?o451Z<)4<k
z59{?Rg2#OKjWB!eiPyyIPhPk>MZ_3dBRjA$A||mOqrf3HabT<vmsCmZF%GH(-$j1?
zNG-qZV`)u>>1GrwsTcJ)-2=(k4O!gBigNmRFjnBZ3189x3r7#}p(fA_Crz#sEYLEV
zKEfD9=q}=hKty|_ed^^{;6rnUa_t^^8GV4((MQ0@b)mhD9{iXa!Z@_Rsz#gWGt6!f
zkhd*%12hgXXE--Y$pXm|hA@e}jD+=x_6*AH(&7knrjg1r%BN1Ls0wGM<rN@1qEZs8
zqDqjcg2UPjmztUY-C;$|l|zC`tt)E8-PYKzq%Pdg5%Ip%^LpG6wZrNRkp&3sLYTU!
zi<)pr;|pEr!fvCT8$ySgo|)dxnm5PkkhJ14Zw;cDwB{uRc}QPxNzAg!Mof2yVUG?o
z^UXe^1BnkZtCaF1Rq;$eIJkeLgCuc&j2}pc?xZ1)#&KrSoflhIj<J7?TfQzUanz@!
ztH5C)IDH<xN-IuBJ<9x=)W(T4CL!OF7Tt>yo-m{MOna;91$LP3sg#OewCjxp2o+dy
z>F6uOE){F~`<|oD`6>X?UEM9dSzIZ;)+>1R+wb(6hO^>(^E}|s<_h2+=7l>ZFr|_5
z!F6aKhsOOeiQ>(pKTl3hB+879lnt2;oXp;U7UR7^X1CXpNf>vIaVIlc?OrD{+iSf}
z>!_p^W9#Xuubi5bVHm~iqLLkzbR3STf1Wi!!OkdHRG<L_HC6NOyrEfHLhDY*63W<F
zWp*cO(oHy{;VAG1V>SwTm&Tk$G+|AkNbBQpn9@XQ(O4FSeQqhJ?Xrl|lC{rTz+o4Q
zm+V)k%ZhF@3hDK!|5RYZQLFSI%D(!IAk03@&YsS;_-NSeh5c<BlVIsoS#IqgZT)ZU
zJmqIq0E2`)3_OM1dA^(yX)sC#i8NK}`|LF!2JSfJla#~eRkc***fr%qhssMFNjzaI
zYOY1s0-bDe5K?wY(Mq_iY}HN5yYr4+gL$%E#0%$&p3~MXcB_~<KTOpXP@dy4zR%PR
z#4G?GGgS@Wkr<Lnc*uBJDXH^4&fWo&#1Ey>hdRm%v^-_dAoTyy>*e98$|>RqRwYXS
z2pibN4)y>qG&B!4wI*ikVDL4jei-(il56vZc%}G#@yfmO=pA*5K@&pTE7q!lTME?L
e{{;Vc|4w@}Ch3rNl-opIc@6}2)vG(JPUkOl`F>;o

literal 4979
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
QBEoh2C;iuhivQYw0gQ*8e*gdg

diff --git a/src/snipper/__pycache__/snip_dir.cpython-38.pyc b/src/snipper/__pycache__/snip_dir.cpython-38.pyc
index a8205b9d82839835b55f286b4d28b2c5c8467e71..4ce3da3636250429478c87ecb3bafc063b86f0dc 100644
GIT binary patch
literal 2057
zcmYjS&2Jk;6rUNdch{S><0Q^UX+SFwNRC8`5K^VKN-HRWQ-nx*F)7x1XJRke4|irv
z5^F3~q!MtT3MT{w$vO7M6%PC%bLGUJKm`(S#tzL|^X9#IAM=~{-i#mA>j8rAX76_V
zQ4^s*jd1nzLAVD)>;j>P;tX|WGwxuJNk;NYr-IE|C3Es>r)qhexgFQqdmYcte#fUC
zty1?1?$jvWLv8;rh>3RFST7{35GC&(rWpg#`6QNu{iJ5p^KT921sD|=A|nVX*!_s`
zO^K%X0!^?a6jP$`8DLQrt9w_%Oi>?@eOH-Olw((S`e@<+@AUCa;H2|Kb?Pd7fyL+2
znR<r9$gcVdUEs+j59<@Gh>h{o>T5<%tLtb&=Jo_^W_+Lk;RWHp$_474;jgf&@;_ua
ztzX8!+)3d{jd}{DcoM)Fd=*gd8hVCkP1T;EU!1c`G#2AI=FXKU;j}T^2M{k#n>4W4
zm*g_!0Z&)x!UfEe1vqi27Ve{m?@#I$Y0J`8WzYcTZ_(H25Yv#>pZLnTkHDv)!s(i-
z%XQT_!y!5ZD~!(ZqxWcIvZz9c_4I&Dnv*3PX+t$H@HrNb<u%oWonN7gM&>VNtLMK%
zgvV;hXaLi4iY6=ax@sO`<tm~)1-Mm3`E==LPp$kwp5jSMwP@qWIVW&#qFRubWz~ed
zR7_sfqG~}67j0C7=~KY8I>+RzRj9@)>_)`wyqjhPB7F>Xe%0_86`OPXt8`@@stMYb
z&9Bv-lSeJJ2%Xy69oIe+MLHZZ{&0M^C?m>*Op92iWf9SoN5`><#E>QFVajME%4j6g
zq8}AyA)vL{Rr_1*imt??6YciWo@6K5Ps%)x3o6F-t!{UCn#9R~b-P<*??yS2!;u`n
zzE_TT!Xhxb8YxOeBnK=Kan7{Uiv=6M`9I8P@3cq;ye!hOK>{FIB1@h!aVr`-(c3#w
z|EC|#_uJjEyR~&=YkMof^V?=7xMRqLu>wPE0O=!z9yZ~op(*;tcK7$>k+X-MBe=5O
zVUfo&8MKKe+wW+?PM%|36~io*+`J(#Zhmm<;DKOV9LUjt9o#RIQO*h}4(4e+5IlLw
zL#n%gczbxNgF9I&B&2A#bK5|1fVhBeylFhG)t#(N;!Nypzcl6MDB}tQ7}vUe9#aNv
z+tW2ZD(o{OI-y}9!6t;zwFEfx{s>(cWi~RE?V6o+%_O!vgeOS{oE<XGiUh2}EH$qd
z>ke2<$7CmZfe+fRX<xE@XyRxuKcYs^)gey{sU4aoQj=0>#|CrR34|Bg&*LLzF?exL
zSd|B2VT$UT(%NXX4^I*{G^n&I1^_S9F3%-rV3d@@Q_DFr1*fXW7#nKGpy%en)J{Ju
zd)hBbp3mZQmd!B4(9#8kek@LPH5IV0u7H>JVO1Qba==X=Xg3uWh09r9e#zQvIuN!c
zP4cvFZiD55S;HEhw~V95PLgayS!XrLSX^|k+#72I|2c>+8M123E8wrm;8>GPbb>fB
zM~-`tOq^+)Yg}l2sNLB*Up5}@(Q(ZCqT>_*t>f|B7SZ+`4^7;LvGOl_#q^?(Kv3Xg
zABV)p4HymyaEJpE0`+kd<_7kOg9BJ|fZG7sXalm0LB?x%8E@1aC&b&}>n=FhsWvb%
zzkeNv{Nq*wurSd=?1FCyehtF!!M<&O7ry=d{oQuJufqa=1Bh<T@4@zb?&fh?%#!b!
zQe|n+){?(vT+Cl1UpM5QA?8$9$n3~>@{*1+wqtIB*aXtFaLlp&3c~h>=JVhG0n(B@
A4gdfE

literal 2103
zcmYjSOK%)S5bo}ootd3|_-(Htibj?|T1zn;KtWMt5F{KegdGlpt<89*cX#kSHr>70
zUiDmXBmxr1TtJW@+1&CUI3w{FTHHAC7os4j?p@27RX?h_9$!^g^*(5|d<5UtAMZp_
z8==27@#<#-@c|6}F-!?caE8L^ibD)E$w+R67S=o~v-4V5Gkcslp=0FT&`sQ=mN<`b
z=p}d;b-jPU7TW1z)re`qOV;bB83oe*Bof2D*wM$cZynYK3=0OI5QL<apd-Roh4zRB
zfap7`vZQUe8v|6?0)Ig+EQv2Me^1~GTkkNMaS3pD3>S7YZcW0zB1WPn5$vbZHLY1;
z!*?VCim)fbO~@Jk3d<V%QFs^iE9+N&B(AiqomkIE<qAKsB}BiDo}$E&&QtWOeRk#7
zU}S$?TbW8$9&j4c)BXTq^P-iw25Vc)NqmMc<`eG{)~XKC`m%lx9eh|d49W#FFUp42
zy~N+3uTdW-^~8VdN&6lGou>3JmSjUL%jOwwqQ1$(IexU9G^&>L!Pf8lq-s}l#?p#x
zU*dDjABt7k2G46r)6n07#=}=U&g9r{vR(Lw-_e?oy?Hrzj@d)Fr&iL2j4Y(6S_Irn
zur4Qa)e4MNu`b(kp-<$hByvSA>Db|(=byQ9`6u!{uGZvQ(m1lu30oAtT!UOL$TsBC
z(z%o^xpo^>Ese=U@F_&lnMF{O9YE0m6ohXWWUpxsSo#dA;B~#nsJJnUwv#Nb!TrPO
zhPf4UdQKi~$QIP?(oWY>Ry00Q&S82e=!x>;GS8zT;ggNsa?D~H01+gVi?oPDS{6ad
z12Lolk8;{2%07%Z4eRtI&c+;$CvSYti*z)i>>$`ZE#e_7i*%9}gCI#6jYY{)%I^fs
zh3cQ!!L99J@XOEo`}6&Yv$^@!=GJD6;q2;z{y@5WmSFG>Oamm*!93J3x<F4%1^+-E
z*}LdDf_8KJMIMQG2*AkJ+ltVW=UCPFC`$$NK>5Y(kM8V0;FR%wF&@(Wdu2S%X(9Oj
z4D>!{@%0`8=>hDm(W&z9W+@ktoYD5X8j5~J97NFMhSoGvce65%GQPcaEy{E#6AKJX
z=>3x@A7zj@F;2i`5SIzv3igJOPyJZvyhdYD0{#@tWT(M^7L-MTCPAN-xk<Vf<Uv{(
zA}zRJW1Z4lSGhgylCWLS;~t}Z%4iW&9=5fkUR;h&SvnYsy{@b3Y+RTZiib_TEyqGf
zz*RklopGXCyv)Y>!h1UKa3L+=hQZJX9<-jmbXC{0p_PB_Z$h?%7kIyWL)l3h3q?w<
zY^|gmdIFZX^78128h37V$jy<bt4aG+_4_9=9cjPH;X?qBg$~OFqg2&KEG<L`6*@XK
z3|pi*uz5!5NZA@D(~oLs53=%5c}2-`W8Gymn<9vyFpC5#zBpC2lmiH5ftK=s%a2n4
z=>o3+FiaPoa#C(kIgIAzm$bVc`rLGdP8YoW&=z{9e8}Kvnnbp&A-JX?5*n_=8I6kG
zD-C2+El>!_@YwW0NHQM!QLF=x4j~CRQ#e<+P`DpDQ+`<M8&7sx^h-zuB$Ts7gYoD%
zVgt?=fME05xm^I<&}F%~N>li(sZZhW5Gn7fV0A-q2@`TW+$J8jNfY}R2CVwm#vZYO
z<6AZ+nEdBh9dqhwIkJdt?BP|sh&wggZsMCjzloi?59`%$Uh84<uWgf;PR$2dunXD_
z*lvP_HetG(y7tb_&R)09)_`C)VG5UL4Y@U2dwG-=(@Sx6@L76jE}I2frLHG!X&S7K
fY`U99d^azXaYnawH*%e{c@v+9y&A&sL+IuIYNJT*

diff --git a/src/snipper/__pycache__/snipper_main.cpython-38.pyc b/src/snipper/__pycache__/snipper_main.cpython-38.pyc
index fd90def87e043144655478d490c81d7f34538bfe..44240208f1eec24853d05c9805203b70e188365e 100644
GIT binary patch
delta 1307
zcmZ8g&2Jk;6n`^bf9%?8ukH0`0Hp<0xCKfNNT@^=N+a<l0&P>ZL@g%WXQxYyKV~Na
zqFF5ATtJGNYPlh#HA37G7sQD(H>8S#uW*6C0Er84Hc3@Ft9kR@?0dg?^M3OlxW6^d
zUAJuuXpFsAw%#lO{Gp9Uhe^h(G=szY=j5ph7SP^jn-#2L5lf_1(ZTXQY`Vy<LC5{!
zryuz=K|BUoqBT$Rvc~w51E?Ir4i^O<!W^>DlAr)NrqDP@9rAn6hIy5b*&M=xD#ZMF
zO<{V@LR%Wjp0PE!eeo6#i(yHX;xuOF46{3i7|M)-uK{yYRiwrI#+We%<dQ`T^K-6j
zRa}9|+FnKGNYAM(77kLOeFKIs8z9XcOZ>_pbQtWl3ChvYGK02)87Ca)b9Mt>0wj;m
zoSX0-4Eaq^Ecjie4w(>&)3r>kZBHRAE8f?6o((Ii99uH~VIj6vC0UCK<}6NOQRm%)
zIiJ%(lfPA2Ta;`G9ZHt06@n+g4Oyae;pjh>4^ott^Z3qD60)ppnV-^O6$;1@497|m
zP*pZj=3;F-HRGYJ(gggibP2}Nwyf#G-lA%R)zBRpm<_A4uJ;mOE*#lV?kp+vAz8{u
zZmTtjn?zDg)rg8ZkyO|BZ(swP`=H!4Ac!XM)=YS-6L0mfr5drNT79N(e#j>O5I7yB
zStRg_+QL?gB2RXd9b4Ge-k7F3+H_1?lW_&E0+C=ICv!)(R9l&6=|=73SuNoc`dQs`
zl1J563B)6T)#%UkcWg8IBJ&l=r?RVTDcZ`u&(24`WM6;R?)#r~z0vqn;cwrSi}c6B
zm%YxCc#fX(!W6s1Zr>mDf<-~Aqg~lWUjz%&Bkkyi9gGIM(jN`G13!?9=#AWy?S&<J
z{hsIzJsmmlz1}eBb?^4JFcP`f*dqEl*U5Z9CqOp(D|ec?QNetEy38!*a*J6hhq+8#
zr{5m+(!-(kG|i;TmPtVtCtXDxFx^d>2YPR;LEY8>>QrN0oFPAtkvJJADA#BsXp-0_
z@7qqurl?_HPNDbt#Gr7t$DdyAuthqM!XJ0Ic#+)2J6g0K3f=qSf)+FbgG2^6?49~w
zU0+xkjJ&NuaETPEk~jKje2MVRllQ_3#sgmt{PkfkpuM*>)LU9ScE&30O=t#5O_<Pj
whi!DsjrF5%y;Bn_I<QP)GrD75CceKny^OA;SdA>}40|TJV4Y4g_}9AfAHFv;;s5{u

delta 895
zcmZ8fO=uHA6n?YWZ1y+1NjAT=PznmM3SJ6QDyS{~K?~MO+t44RFR8I5rJD&tnKfW~
z5V2TUML~qk)th)01aIO+kREapy!YhMgKv|9#aU+O{eJW2d+&SxhdXxHvUH$T_%?oT
zBnj{<Qcf<Fl*_c6PhO1iqJt({d#sYi6eiIoDTC||l+zzRf0WwViNq4g2JNXRcU1xq
zD#E7ZnRW10WWL5h07@7`c|Ud_J$HOv=ppNZZwMolj?WmzyUaJaEX)p@hLwqB$+!HZ
zNQQA#x(pMWve)2>fVY5ZTUc~x@8k|+Kp_d#(dbIT6xJl{Oz)<-O0p^vXzs^+a{(F`
zW#HPT?tNw8+YEL~1Z79rTtQ1fM<N{iF<XFfK#F)|FTxTuq(u;{c_?BB%)7q<z9Xbs
zRGfX*PYEZ~x$(dZEs+YI%0oAQ3I`KKpki!vs2I{FDV<VpahuA4fge-*F}l|I-!iy_
zr-T_C$@#X48FVqb5$j04DdL3khP#xbMLN$$EG^U1{Is8G$e8fcJQvyev683oyvTI=
ztR0XhWa^04mmn+<<F0Ut*BmjPrDx?ZkA*!DnQ0(|JoO;o?l&PvP00EMkqdQEsIe~b
zb-;Fx2;9VCk%FVP5bUThLmf+zALB%bNx;R3%iAyqi_jl{Av_fgz;XMFq8LU0ixFuW
zuHo5hJ?8b~77tD<uURknq`YKH!Cc}hD+R9-tL#GXJ8|Pm{GnIp)pE%@M<Z9!yjEka
zR^O^ND`q5axA+>?y=FyQCu!iYn%i3~Uf*u4ZPlB+5<F9f&5G_-YgMn>Sg$sNFY0wx
z2@I{QEE6Uu!Gt!-^k79hUrsZfxst<N=FO0+%;wAL{OsT>yWS<Tc`5q!f<t|ZYAzY;
RY$oU!XV^&a-WX+n{sLr9+`9k(

diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py
index cfad9c4..23d4271 100644
--- a/src/snipper/block_parsing.py
+++ b/src/snipper/block_parsing.py
@@ -20,31 +20,41 @@ def block_iterate(lines, tag):
 
         yield  contents
 
+def block_join(contents):
+    blk = contents['block']
+    if len(blk) == 1:
+        blk = [blk[0] + contents['post1'] + " " + contents['post2']]
+    elif len(blk) > 1:
+        blk = [blk[0] + contents['post1']] + blk[1:-1] + [blk[-1] + contents['post2']]
+    return contents['first'] + blk + contents['last']
+
 
 def block_split(lines, tag):
     stag = tag[:2]  # Start of any next tag.
 
-    def join(contents):
-        return contents['first'] + [contents['block'][0] + contents['post1']] + contents['block'][1:-1] \
-                + [contents['block'][-1] + contents['post2']] + contents['last']
+
     contents = {}
     i, j = f2(lines, tag)
 
     def get_tag_args(line):
         # line = line.strip()
         k = line.find(" ")
-        tag_args = ((line[:k + 1] if k >= 0 else line)[len(tag):] ).strip()
+        tag_args = ((line[:k + 1] if k >= 0 else line)[len(tag):] ).strip().split(";")
+        tag_args = [t.strip() for t in tag_args]
+        # if len(tag_args) == 0:
+        #     return {'': ''}  # No name.
+        tag_args = [t for t in tag_args if len(t) > 0]
+        tag_args = dict([t.split("=") if "=" in t else (t.lower().strip(), True) for t in tag_args])
 
-        if len(tag_args) == 0:
-            return {'': ''}  # No name.
+        if '' not in tag_args:
+            tag_args[''] = ''
 
-        tag_args = dict([t.split("=") for t in tag_args.split(";")])
         return tag_args
 
     if i is None:
         return None
     else:
-        print( lines[i] )
+        # print( lines[i] )
 
         start_tag_args = get_tag_args(lines[i][j:])
         START_TAG = f"{tag}={start_tag_args['']}" if start_tag_args[''] != '' else tag
@@ -62,7 +72,7 @@ def block_split(lines, tag):
         l2 = lines[:i] + [lines[i][:j2], lines[i][j2:]] + lines[i2+1:]
         c2 = block_split(l2, tag=tag)
         c2['block'].pop()
-        c2['joined'] = join(c2)
+        c2['joined'] = block_join(c2)
         return c2
     else:
         contents['first'] = lines[:i]
@@ -81,7 +91,7 @@ def block_split(lines, tag):
         contents['arg2'], contents['post2'] = argpost(lines[i2], j2)
         blk = [lines[i][:j]] + lines[i+1:i2] + [lines[i2][:j2]]
         contents['block'] = blk
-        contents['joined'] = join(contents)
+        contents['joined'] = block_join(contents)
         contents['start_tag_args'] = start_tag_args
         contents['name'] = start_tag_args['']
         return contents
diff --git a/src/snipper/fix_bf.py b/src/snipper/fix_bf.py
index 3a128bc..c4d5254 100644
--- a/src/snipper/fix_bf.py
+++ b/src/snipper/fix_bf.py
@@ -1,6 +1,7 @@
 import functools
-from snipper.legacy import gcoms, block_process
+from snipper.legacy import gcoms
 from snipper.block_parsing import indent
+from snipper.block_parsing import block_split, block_join
 
 
 def fix_f(lines, debug):
@@ -29,39 +30,53 @@ def fix_f(lines, debug):
             comments = [id + c for c in comments]
             if len(comments) > 0:
                 lines2 += comments[0].split("\n")
-            # lines2 += [id+"#!b"]
             f = [id + l.strip() for l in funrem.splitlines()]
             f[0] = f[0] + "#!b"
-
-            # lines2 += (id+funrem.strip()).split("\n")
             errm = l_head if len(l_head) > 0 else "Implement function body"
             f[-1] = f[-1] + f' #!b {errm}'
             lines2 += f
-            # lines2 += [f'{id}#!b {errm}']
-
         else:
             lines2.append(l)
             i += 1
     return lines2
 
+# stats = {'n': 0}
+def _block_fun(lines, start_extra, end_extra, keep=False, silent=False):
+    id = indent(lines[0])
+    lines = lines[1:] if len(lines[0].strip()) == 0 else lines
+    lines = lines[:-1] if len(lines[-1].strip()) == 0 else lines
+    cc = len(lines)
+    ee = end_extra.strip()
+    if len(ee) >= 2 and ee[0] == '"':
+        ee = ee[1:-1]
+    start_extra = start_extra.strip()
+    if keep:
+        l2 = ['GARBAGE'] * cc
+    else:
+        if silent:
+            l2 = []
+            cc = 0
+        else:
+            l2 = ([id + start_extra] if len(start_extra) > 0 else []) + [id + f"# TODO: {cc} lines missing.",
+                                                                         id + f'raise NotImplementedError("{ee}")']
+
+    # stats['n'] += cc
+    return l2, cc
+
+
 
 def fix_b2(lines, keep=False):
-    stats = {'n': 0}
-    def block_fun(lines, start_extra, end_extra, art, stats=None, **kwargs):
-        id = indent(lines[0])
-        lines = lines[1:] if len(lines[0].strip()) == 0 else lines
-        lines = lines[:-1] if len(lines[-1].strip()) == 0 else lines
-        cc = len(lines)
-        ee = end_extra.strip()
-        if len(ee) >= 2 and ee[0] == '"':
-            ee = ee[1:-1]
-        start_extra = start_extra.strip()
-        if keep:
-            l2 = ['GARBAGE'] * cc
-        else:
-            l2 = ([id+start_extra] if len(start_extra) > 0 else []) + [id + f"# TODO: {cc} lines missing.", id+f'raise NotImplementedError("{ee}")']
+    cutout = []
+    n = 0
+    while True:
+        b = block_split(lines, tag="#!b")
+        if b == None:
+            break
+        args = {k:v for k, v in b['start_tag_args'].items() if len(k) > 0}
+        cutout += b['block']
+        b['block'], dn = _block_fun(b['block'], start_extra=b['arg1'], end_extra=b['arg2'], **args, keep=keep)
+        lines = block_join(b)
+        n += dn
 
-        stats['n'] += cc
-        return l2, cc
-    lines2, _, _, cutout = block_process(lines, tag="#!b", block_fun=functools.partial(block_fun, stats=stats))
-    return lines2, stats['n'], cutout
\ No newline at end of file
+    # lines2, _, _, cutout = block_process(lines, tag="#!b", block_fun=functools.partial(block_fun, stats=stats))
+    return lines, n, cutout
\ No newline at end of file
diff --git a/src/snipper/fix_cite.py b/src/snipper/fix_cite.py
index c4cfb1c..4b49641 100644
--- a/src/snipper/fix_cite.py
+++ b/src/snipper/fix_cite.py
@@ -19,7 +19,7 @@ def fix_aux_special(lines, aux, command='\\nref', output='\cite[%s]{my_bibtex_en
 
 def fix_aux(lines, aux, strict=True):
     l2 = fix_single_reference(lines, aux=aux, cmd="\\ref", strict=True)
-    print("\n".join(l2))
+    # print("\n".join(l2))
     return l2
 
 def fix_bibtex(lines, bibtex):
diff --git a/src/snipper/fix_i.py b/src/snipper/fix_i.py
index 0d17a39..28b5bfb 100644
--- a/src/snipper/fix_i.py
+++ b/src/snipper/fix_i.py
@@ -2,6 +2,11 @@ import functools
 import textwrap
 from snipper.legacy import block_process
 from snipper.block_parsing import full_strip
+import os
+if os.name == 'nt':
+    import wexpect as we
+else:
+    import pexpect as we
 
 
 def run_i(lines, file, output):
@@ -14,11 +19,6 @@ def run_i(lines, file, output):
         lines = textwrap.dedent(s).strip().splitlines()
 
         if extra['python'] is None:
-            import os
-            if os.name == 'nt':
-                import wexpect as we
-            else:
-                import pexpect as we
             an = we.spawn("python", encoding="utf-8", timeout=20)
             an.expect([">>>"])
             extra['python'] = an
@@ -34,8 +34,6 @@ def run_i(lines, file, output):
 
             lines = l2
             alines = []
-
-            # indented = False
             in_dot_mode = False
             if len(lines[-1]) > 0 and (lines[-1].startswith(" ") or lines[-1].startswith("\t")):
                 lines += [""]
@@ -57,14 +55,8 @@ def run_i(lines, file, output):
                 if 'help(s.find)' in word:
                     pass
                 if dotmode:
-                    # alines.append("..." + word)
                     alines.append(">>>" + analyzer.before.rstrip() if not in_dot_mode else "..." + analyzer.before.rstrip())
                     in_dot_mode = True
-                    # if i < len(lines) - 1 and not lines[i + 1].startswith(" "):
-                    #     analyzer.sendline("\n")  # going out of indentation mode .
-                    #     analyzer.expect_exact([">>>", "..."])
-                    #     alines.append("..." + analyzer.after.rstrip())
-                    #     pass
                 else:
                     alines.append( ("..." if in_dot_mode else ">>>") + analyzer.before.rstrip())
                     in_dot_mode = False
diff --git a/src/snipper/fix_s.py b/src/snipper/fix_s.py
index c740280..5a97096 100644
--- a/src/snipper/fix_s.py
+++ b/src/snipper/fix_s.py
@@ -1,26 +1,47 @@
 from collections import defaultdict
 import os
 from snipper.block_parsing import block_iterate
+from snipper.snipper_main import full_strip
 
 def get_s(lines):
     """ Return snips from 'lines' """
     blocks = defaultdict(list)
     for c in block_iterate(lines, "#!s"):
+        # c['start_tag_args']
+        if not c['start_tag_args'].get('keeptags', False):
+            c['block'] = full_strip(c['block'])
+        else:
+            # In this case the #! tags are kept in.
+            pass
+            # print("keepting tags.")
         blocks[c['name']].append(c)
 
     output = {}
     for name, co in blocks.items():
-        output[name] = [l for c in co for l in c['block']]
+        slines = [l for c in co for l in c['block']]
+        # full_strip("")
+        # c['block']['args']
+        # slines = slines[ 23]
+        # co.
+        output[name] = slines
     return output
 
+# def _s_block_process():
+#
+#     pass
+
 def save_s(lines, output_dir, file_path): # save file snips to disk
     content = get_s(lines)
-    if not os.path.isdir(output_dir):
+    # Only make output dir if needed.
+    if len(content) > 0 and not os.path.isdir(output_dir):
         os.mkdir(output_dir)
+
     for name, ll in content.items():
         if file_path is not None:
+            file_path = file_path.replace("\\", "/")
             ll = [f"# {file_path}"] + ll
         out = "\n".join(ll)
+
         with open(output_dir + "/" + os.path.basename(file_path)[:-3] + ("_" + name if len(name) > 0 else name) + ".py", 'w') as f:
             f.write(out)
 
diff --git a/src/snipper/load_citations.py b/src/snipper/load_citations.py
index 786c02b..0512cd1 100644
--- a/src/snipper/load_citations.py
+++ b/src/snipper/load_citations.py
@@ -1,18 +1,17 @@
 import os
 import io
-# from coursebox.core.info_paths import get_paths
 from pybtex import plugin
 from pybtex.database.input import bibtex
+from warnings import warn
 
 ### Newstyle loading.
-
 def get_aux(auxfile):
     # paths = get_paths()
     # auxfile = os.path.join(paths['02450public'], auxfile)
     if not os.path.exists(auxfile):
-        print(auxfile)
-        from warnings import warn
-        warn("Could not find file")
+        # print(auxfile)
+
+        warn("Could not find bibtex file: "+ auxfile)
         return {}
 
     with open(auxfile, 'r') as f:
@@ -104,17 +103,7 @@ def get_bibtex(bibfile):
                            'filename': url,
                            }
 
-    # newref = {}
-    # ls = lambda x: x if isinstance(x, list) else [x]
-    # if 'tex_command' in gi:
-    #     for cmd, aux, display in zip( ls(gi['tex_command']), ls(gi['tex_aux'] ), ls( gi['tex_display'] ) ):
-    #         ax = parse_aux(aux, bibtex=gi['bibtex'])
-    #         for k in ax:
-    #             ax[k]['pyref'] = display%(ax[k]['nicelabel'],)
-    #         newref[cmd] = ax
-
-    return refs#, newref
-
+    return refs
 
 
 def find_tex_cite(s, start=0, key="\\cite"):
@@ -132,112 +121,112 @@ def find_tex_cite(s, start=0, key="\\cite"):
     return (i, j), reference, txt
 
 ### Oldstyle loading
-def get_references(bibfile, gi):
-    """
-    all references.
-    """
-    if not os.path.exists(bibfile):
-        return None
-
-    pybtex_style = plugin.find_plugin('pybtex.style.formatting', 'alpha')()
-    pybtex_html_backend = plugin.find_plugin('pybtex.backends', 'html')()
-    pybtex_plain_backend = plugin.find_plugin('pybtex.backends', 'plaintext')()
-    pybtex_parser = bibtex.Parser()
-
-    with open(bibfile, 'r', encoding='utf8') as f:
-        data = pybtex_parser.parse_stream(f)
-
-    data_formatted = pybtex_style.format_entries(data.entries.values())
-    refs = {}
-
-    if 'auxfile' in gi:
-        all_references = parse_aux(gi['auxfile'], bibtex=gi['bibtex'])
-    else:
-        all_references = {}
-
-    for entry in data_formatted:
-        output = io.StringIO()
-        output_plain = io.StringIO()
-        pybtex_plain_backend.output = output_plain.write
-        pybtex_html_backend.output = output.write
-        pybtex_html_backend.write_entry(entry.key, entry.label, entry.text.render(pybtex_html_backend))
-
-        pybtex_plain_backend.write_entry(entry.key, entry.label, entry.text.render(pybtex_plain_backend))
-
-        html = output.getvalue()
-        plain = output_plain.getvalue()
-
-        entry.text.parts[-2].__str__()
-        url = ""
-        for i,p in enumerate(entry.text.parts):
-            if "\\url" in p.__str__():
-                url = entry.text.parts[i+1]
-                break
-        url = url.__str__()
-        i1 = html.find("\\textbf")
-        i2 = html.find("</span>", i1)
-        dht = html[i1:i2]
-        dht = dht[dht.find(">")+1:]
-        html = html[:i1] + " <b>"+dht+"</b> " + html[i2+7:]
-
-        plain = plain.replace("\\textbf ", "")
-        iu = plain.find("URL")
-        if iu > 0:
-            plain = plain[:iu]
-
-        refs[entry.key] = {'html': html,
-                           'plain': plain,
-                           'label': entry.label,
-                           'filename': url,
-                           'references': all_references}
-
-    newref = {}
-    ls = lambda x: x if isinstance(x, list) else [x]
-    if 'tex_command' in gi:
-        for cmd, aux, display in zip( ls(gi['tex_command']), ls(gi['tex_aux'] ), ls( gi['tex_display'] ) ):
-            ax = parse_aux(aux, bibtex=gi['bibtex'])
-            for k in ax:
-                ax[k]['pyref'] = display%(ax[k]['nicelabel'],)
-            newref[cmd] = ax
-
-    return refs, newref
-
-
-def parse_aux(auxfile, bibtex=None):
-    # paths = get_paths()
-    paths = {}
-    auxfile = os.path.join(paths['02450public'], auxfile)
-    if not os.path.exists(auxfile):
-        print(auxfile)
-        from warnings import warn
-        warn("Could not find file")
-        return {}
-
-    with open(auxfile, 'r') as f:
-        items = f.readlines()
-    entries = {}
-    for e in items:
-        e = e.strip()
-        if e.startswith("\\newlabel") and "@cref" in e:
-            # print(e)
-            i0 = e.find("{")
-            i1 = e.find("@cref}")
-            key = e[i0+1:i1]
-
-            j0 = e.find("{{[", i0)+3
-            j1 = e.find("}", j0)
-
-            val = e[j0:j1]
-
-            label = val[:val.find("]")]
-            number = val[val.rfind("]")+1:]
-
-            if label == "equation":
-                nlabel = f"eq. ({number})"
-            else:
-                nlabel = label.capitalize() + " " + number
-
-            coderef = "\\cite[%s]{%s}"%(nlabel, bibtex) if bibtex is not None else None
-            entries[key] = {'pyref': coderef, 'nicelabel': nlabel, 'rawlabel': label, 'number': number}
-    return entries
+# def get_references(bibfile, gi):
+#     """
+#     all references.
+#     """
+#     if not os.path.exists(bibfile):
+#         return None
+#
+#     pybtex_style = plugin.find_plugin('pybtex.style.formatting', 'alpha')()
+#     pybtex_html_backend = plugin.find_plugin('pybtex.backends', 'html')()
+#     pybtex_plain_backend = plugin.find_plugin('pybtex.backends', 'plaintext')()
+#     pybtex_parser = bibtex.Parser()
+#
+#     with open(bibfile, 'r', encoding='utf8') as f:
+#         data = pybtex_parser.parse_stream(f)
+#
+#     data_formatted = pybtex_style.format_entries(data.entries.values())
+#     refs = {}
+#
+#     if 'auxfile' in gi:
+#         all_references = parse_aux(gi['auxfile'], bibtex=gi['bibtex'])
+#     else:
+#         all_references = {}
+#
+#     for entry in data_formatted:
+#         output = io.StringIO()
+#         output_plain = io.StringIO()
+#         pybtex_plain_backend.output = output_plain.write
+#         pybtex_html_backend.output = output.write
+#         pybtex_html_backend.write_entry(entry.key, entry.label, entry.text.render(pybtex_html_backend))
+#
+#         pybtex_plain_backend.write_entry(entry.key, entry.label, entry.text.render(pybtex_plain_backend))
+#
+#         html = output.getvalue()
+#         plain = output_plain.getvalue()
+#
+#         entry.text.parts[-2].__str__()
+#         url = ""
+#         for i,p in enumerate(entry.text.parts):
+#             if "\\url" in p.__str__():
+#                 url = entry.text.parts[i+1]
+#                 break
+#         url = url.__str__()
+#         i1 = html.find("\\textbf")
+#         i2 = html.find("</span>", i1)
+#         dht = html[i1:i2]
+#         dht = dht[dht.find(">")+1:]
+#         html = html[:i1] + " <b>"+dht+"</b> " + html[i2+7:]
+#
+#         plain = plain.replace("\\textbf ", "")
+#         iu = plain.find("URL")
+#         if iu > 0:
+#             plain = plain[:iu]
+#
+#         refs[entry.key] = {'html': html,
+#                            'plain': plain,
+#                            'label': entry.label,
+#                            'filename': url,
+#                            'references': all_references}
+#
+#     newref = {}
+#     ls = lambda x: x if isinstance(x, list) else [x]
+#     if 'tex_command' in gi:
+#         for cmd, aux, display in zip( ls(gi['tex_command']), ls(gi['tex_aux'] ), ls( gi['tex_display'] ) ):
+#             ax = parse_aux(aux, bibtex=gi['bibtex'])
+#             for k in ax:
+#                 ax[k]['pyref'] = display%(ax[k]['nicelabel'],)
+#             newref[cmd] = ax
+#
+#     return refs, newref
+#
+#
+# def parse_aux(auxfile, bibtex=None):
+#     # paths = get_paths()
+#     paths = {}
+#     auxfile = os.path.join(paths['02450public'], auxfile)
+#     if not os.path.exists(auxfile):
+#         print(auxfile)
+#         from warnings import warn
+#         warn("Could not find file")
+#         return {}
+#
+#     with open(auxfile, 'r') as f:
+#         items = f.readlines()
+#     entries = {}
+#     for e in items:
+#         e = e.strip()
+#         if e.startswith("\\newlabel") and "@cref" in e:
+#             # print(e)
+#             i0 = e.find("{")
+#             i1 = e.find("@cref}")
+#             key = e[i0+1:i1]
+#
+#             j0 = e.find("{{[", i0)+3
+#             j1 = e.find("}", j0)
+#
+#             val = e[j0:j1]
+#
+#             label = val[:val.find("]")]
+#             number = val[val.rfind("]")+1:]
+#
+#             if label == "equation":
+#                 nlabel = f"eq. ({number})"
+#             else:
+#                 nlabel = label.capitalize() + " " + number
+#
+#             coderef = "\\cite[%s]{%s}"%(nlabel, bibtex) if bibtex is not None else None
+#             entries[key] = {'pyref': coderef, 'nicelabel': nlabel, 'rawlabel': label, 'number': number}
+#     return entries
 
diff --git a/src/snipper/snip_dir.py b/src/snipper/snip_dir.py
index 328e93b..f264c59 100644
--- a/src/snipper/snip_dir.py
+++ b/src/snipper/snip_dir.py
@@ -3,13 +3,21 @@ from snipper.snipper_main import censor_file
 from pathlib import Path
 import time
 import fnmatch
-
+import tempfile
 
 def snip_dir(source_dir,  # Sources
-             dest_dir,  # Will write to this directory
+             dest_dir=None,  # Will write to this directory
              output_dir=None, # Where snippets are going to be stored
              references=None, # Reference database
-             exclude=None, clean_destination_dir=True):
+             exclude=None, clean_destination_dir=True,
+             run_files=True,  # Run #!o tags and #!i tags
+             cut_files=True,   # censor files.
+             license_head=None,
+             ):
+
+    if dest_dir == None:
+        dest_dir = tempfile.mkdtemp()
+        print("[snipper]", "no destination dir was specified so using nonsense destination:", dest_dir)
 
     if references == None:
         references = dict(aux=None, bibtex=None,  commands=[])
@@ -17,14 +25,16 @@ def snip_dir(source_dir,  # Sources
     if exclude == None:
         exclude = []
 
+    exclude += ["*__pycache__*"]  # Just...no.
     if not os.path.exists(dest_dir):
         os.makedirs(dest_dir)
-    if not os.path.exists(output_dir):
-        os.makedirs(output_dir)
 
-    output_dir = os.path.abspath(output_dir)
     source_dir = os.path.abspath(source_dir)
     dest_dir = os.path.abspath(dest_dir)
+    if output_dir == None:
+        output_dir = os.path.dirname(source_dir) + "/output"
+
+    output_dir = os.path.abspath(output_dir)
     if os.path.samefile( source_dir, dest_dir):
         raise Exception("Source and destination is the same")
 
@@ -33,15 +43,14 @@ def snip_dir(source_dir,  # Sources
     os.makedirs(dest_dir)
 
     out = dest_dir
-    hw = {'base': source_dir,
-          'exclusion': exclude}
+    hw = {'base': source_dir}
 
     print(f"[snipper] Synchronizing directories: {hw['base']} -> {out}")
     if os.path.exists(dest_dir):
         shutil.rmtree(dest_dir)
 
     shutil.copytree(source_dir, dest_dir)
-    time.sleep(0.2)
+    time.sleep(0.1)
 
     ls = list(Path(dest_dir).glob('**/*.*'))
     acceptable = []
@@ -50,6 +59,10 @@ def snip_dir(source_dir,  # Sources
         m = [fnmatch.fnmatch(split, ex) for ex in exclude]
         acceptable.append( (l, not any(m) ))
 
+    # for f,ac in acceptable:
+    #     if not ac:
+    #         print(f)
+
     # print(acceptable)
     # now we have acceptable files in list.
     # run_out_dirs = ["./output"]
@@ -58,7 +71,7 @@ def snip_dir(source_dir,  # Sources
     # edirs = {os.path.normpath(os.path.dirname(f_) if not os.path.isdir(f_) else f_) for f_ in edirs}
     # edirs.remove(os.path.normpath(out))
     for f, accept in acceptable:
-        if os.path.isdir(f) or not str(f).endswith(".py"): # We only touch .py files.
+        if os.path.isdir(f) or not str(f).endswith(".py") or  str(f).endswith("_grade.py"): # We only touch .py files.
             continue
         # f_dir = os.path.normpath(f if os.path.isdir(f) else os.path.dirname(f))
         if accept:
@@ -70,15 +83,18 @@ def snip_dir(source_dir,  # Sources
 
             # if "assignments" in str(f) and "_grade.py" in str(f):
             #     continue
-            info = {'new_references': [], 'code_copyright': 'Example student code. This file is automatically generated from the files in the instructor-directory'}
+            # info = {'new_references': [], 'code_copyright': 'Example student code. This file is automatically generated from the files in the instructor-directory'}
             # paths = {}
             solution_list = []
             kwargs = {}
-            cut_files = True
-            run_files = True
+            # cut_files = True
+            # copyright()
+
+            # run_files = True
             nrem = censor_file(f, run_files=run_files, run_out_dirs=output_dir, cut_files=cut_files, solution_list=solution_list,
                                base_path=dest_dir,
                                references=references,
+                               license_head=license_head,
                                **kwargs)
             if nrem > 0:
                 print(f"{nrem}> {f}")
diff --git a/src/snipper/snipper_main.py b/src/snipper/snipper_main.py
index 4b9e7da..b2f5803 100644
--- a/src/snipper/snipper_main.py
+++ b/src/snipper/snipper_main.py
@@ -33,7 +33,8 @@ def censor_file(file, run_files=True, run_out_dirs=None, cut_files=True, solutio
                 censor_files=True,
                 base_path=None,
                 strict=True,
-                references=None):
+                references=None,
+                license_head=None):
 
     if references == None:
         references = {}
@@ -55,7 +56,7 @@ def censor_file(file, run_files=True, run_out_dirs=None, cut_files=True, solutio
             print("Error in file, cite/reference tag not found!>", file)
             raise e
 
-        if run_files or cut_files:
+        if (run_files or cut_files) and run_out_dirs is not None:
             ofiles = []
             for rod in [run_out_dirs]:
                 ofiles.append(os.path.join(rod, os.path.basename(file).split(".")[0]) )
@@ -96,12 +97,20 @@ def censor_file(file, run_files=True, run_out_dirs=None, cut_files=True, solutio
                 #     with open(sout, "w") as f:
                 #         f.write(sol)
 
-        if len(lines[-1])>0:
+        if len(lines) > 0 and len(lines[-1])>0:
             lines.append("")
         s2 = "\n".join(lines)
 
+    if license_head is not None:
+        s2 = fix_copyright(s2, license_head)
+
+
     with open(file, 'w', encoding='utf-8') as f:
         f.write(s2)
     return nB
-# lines: 294, 399, 420, 270
 
+
+def fix_copyright(s, license_head):
+    return "\n".join( ["# " + l.strip() for l in license_head.splitlines()] ) +"\n" + s
+
+# lines: 294, 399, 420, 116
\ No newline at end of file
-- 
GitLab