diff --git a/dist/codesnipper-0.0.2-py3-none-any.whl b/dist/codesnipper-0.0.2-py3-none-any.whl
new file mode 100644
index 0000000000000000000000000000000000000000..a3de71167725d94f82ba94271db109d7a5baa198
Binary files /dev/null and b/dist/codesnipper-0.0.2-py3-none-any.whl differ
diff --git a/dist/codesnipper-0.0.2.tar.gz b/dist/codesnipper-0.0.2.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..128343f9db99e65419e5a8c62fb3bed6fa91d662
Binary files /dev/null and b/dist/codesnipper-0.0.2.tar.gz differ
diff --git a/docs/build_docs.py b/docs/build_docs.py
index 02f77913273a5c32c9672b84bfd54b06cdb79912..46e7a45ce1114a3e4007293330f919b5f36d400e 100644
--- a/docs/build_docs.py
+++ b/docs/build_docs.py
@@ -20,10 +20,8 @@ if __name__ == "__main__":
     from snipper.fix_s import save_s
     from snipper.snipper_main import censor_file
 
-
-    #
-    # EX_BASE = "../examples"
-    # np = EX_BASE + "/new_project"
+    EX_BASE = "../examples"
+    np = EX_BASE + "/latex"
 
     # if os.path.isdir(np):
     #     shutil.rmtree(np)
@@ -40,7 +38,7 @@ if __name__ == "__main__":
     # my_nup(np + "/index.pdf")
     # my_nup(f"{np_basic1}/index.pdf")
     #
-    # # convert.pdf2png(np + "/index.pdf", "./index0.png")
+    convert.pdf2png(np + "/index.pdf", "./index.png", scale_to=600)
     # output = np +"/index_2up.pdf"
     #
     # data = {}
diff --git a/example/__pycache__/load_references.cpython-38.pyc b/examples/__pycache__/load_references.cpython-38.pyc
similarity index 100%
rename from example/__pycache__/load_references.cpython-38.pyc
rename to examples/__pycache__/load_references.cpython-38.pyc
diff --git a/examples/block_processor.py b/examples/block_processor.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8662f7f5baa7c158e26f8565a62c8bf66e67acb
--- /dev/null
+++ b/examples/block_processor.py
@@ -0,0 +1,63 @@
+from snipper.fix_s import get_s #!s
+from snipper.block_parsing import block_split
+import textwrap
+import numpy as np
+
+# Implement a sieve here.
+def primes_sieve(limit):
+    limitn = limit+1 #!b
+    primes = range(2, limitn)
+
+    for i in primes:
+        factors = list(range(i, limitn, i))
+        for f in factors[1:]:
+            if f in primes:
+                primes.remove(f)
+    return primes #!b
+#!s
+
+def obscure(blk, fun):
+    blok = block_split(blk, "#!b")
+    lines2 = blok['first'] + fun(blok['block']) + blok['last']
+    s = '\n'.join(lines2)
+    print(s)
+    return s
+
+# driver program
+if __name__ == '__main__':
+    with open(__file__, 'r') as f:
+        s = f.read().splitlines()
+    blk = get_s(s)['']
+
+    def cmnt(lines):
+        whitespace = " " * (len(lines[0]) - len(lines[0].lstrip()))
+        lines = textwrap.dedent("\n".join(lines)).splitlines()
+        lines = ["# " + l for l in lines]
+        return lines, whitespace
+
+    def f1(lines):
+        lines, whitespace = cmnt(lines)
+        lines = [lines[i] for i in np.random.permutation(len(lines))]
+        lines = textwrap.indent("\n".join(lines), whitespace).splitlines()
+        return lines
+
+    obscure(blk, f1)
+
+    def f2(lines):
+        lines, whitespace = cmnt(lines)
+        kp = """#'"[](){},.+-012345679:="""
+        l2 = []
+        for line in lines:
+            line2 = []
+            for w in line.split(' '):
+                if w in ['', 'return', 'if', 'else' '=', '#', "for", "in"]:
+                    line2.append(w)
+                else:
+                    w2 = "".join( [ (t if t in kp else '?') for t in w] )
+                    line2.append(w2)
+            l2.append(" ".join(line2))
+        lines = l2
+        lines = textwrap.indent("\n".join(lines), whitespace).splitlines()
+        return lines
+
+    obscure(blk, f2)
diff --git a/example/citations.py b/examples/citations.py
similarity index 100%
rename from example/citations.py
rename to examples/citations.py
diff --git a/examples/cs101_instructor/homework1.py b/examples/cs101_instructor/homework1.py
new file mode 100644
index 0000000000000000000000000000000000000000..49ed989a2f83b491f310646921c0cf224eb99503
--- /dev/null
+++ b/examples/cs101_instructor/homework1.py
@@ -0,0 +1,20 @@
+def myfun(): #!s
+    """
+    Simple aux references \ref{eq1} in \ref{sec1}.
+    Simple bibtex citations: \cite{bertsekasII} and \cite[Somewhere around the middle]{herlau}
+
+    Example of custom command (reference notes)
+    > \nref{fig1}
+
+    Other example of custom command (reference assignment)
+    > \aref2{sec1}
+    """
+    print("See \\ref{sec1}")  # Also works.
+    return 42 #!s
+
+def fun1(l1, l2):
+    s1 = sum(l1) #!s
+    s2 = sum(l2)
+    print("Hello worlds!") #!s
+    return s1 + s2
+
diff --git a/example/exercise1.py b/examples/exercise1.py
similarity index 100%
rename from example/exercise1.py
rename to examples/exercise1.py
diff --git a/example/latex/br.pdf b/examples/latex/br.pdf
similarity index 100%
rename from example/latex/br.pdf
rename to examples/latex/br.pdf
diff --git a/example/latex/index.aux b/examples/latex/index.aux
similarity index 100%
rename from example/latex/index.aux
rename to examples/latex/index.aux
diff --git a/example/latex/index.bbl b/examples/latex/index.bbl
similarity index 100%
rename from example/latex/index.bbl
rename to examples/latex/index.bbl
diff --git a/example/latex/index.blg b/examples/latex/index.blg
similarity index 100%
rename from example/latex/index.blg
rename to examples/latex/index.blg
diff --git a/example/latex/index.log b/examples/latex/index.log
similarity index 100%
rename from example/latex/index.log
rename to examples/latex/index.log
diff --git a/example/latex/index.out b/examples/latex/index.out
similarity index 100%
rename from example/latex/index.out
rename to examples/latex/index.out
diff --git a/example/latex/index.pdf b/examples/latex/index.pdf
similarity index 100%
rename from example/latex/index.pdf
rename to examples/latex/index.pdf
diff --git a/example/latex/index.synctex.gz b/examples/latex/index.synctex.gz
similarity index 100%
rename from example/latex/index.synctex.gz
rename to examples/latex/index.synctex.gz
diff --git a/example/latex/index.tex b/examples/latex/index.tex
similarity index 100%
rename from example/latex/index.tex
rename to examples/latex/index.tex
diff --git a/example/latex/library.bib b/examples/latex/library.bib
similarity index 100%
rename from example/latex/library.bib
rename to examples/latex/library.bib
diff --git a/example/load_references.py b/examples/load_references.py
similarity index 93%
rename from example/load_references.py
rename to examples/load_references.py
index 9718dde77f21095e8477d4437f3b1765c8f7f911..aad5a0fead00b9240c8387f9f28dc6f5ec17b592 100644
--- a/example/load_references.py
+++ b/examples/load_references.py
@@ -3,7 +3,7 @@ import os
 
 def reference_example():
     # Load references and insert them.
-    from snipper.citations import get_bibtex, get_aux #!s=a
+    from snipper.load_citations import get_bibtex, get_aux #!s=a
     bibfile = "latex/library.bib"
     auxfile = 'latex/index.aux'
     bibtex = get_bibtex(bibfile)
diff --git a/example/output/citations.py b/examples/output/citations.py
similarity index 100%
rename from example/output/citations.py
rename to examples/output/citations.py
diff --git a/example/output/load_references_a.py b/examples/output/load_references_a.py
similarity index 71%
rename from example/output/load_references_a.py
rename to examples/output/load_references_a.py
index 304c800fad3b5988a1f866385416f228499815f1..672cac255810264aa57dc7339c0dbb8134fdf56e 100644
--- a/example/output/load_references_a.py
+++ b/examples/output/load_references_a.py
@@ -1,5 +1,5 @@
 # load_references.py
-    from snipper.citations import get_bibtex, get_aux 
+    from snipper.load_citations import get_bibtex, get_aux
     bibfile = "latex/library.bib"
     auxfile = 'latex/index.aux'
     bibtex = get_bibtex(bibfile)
diff --git a/example/output/load_references_b.py b/examples/output/load_references_b.py
similarity index 100%
rename from example/output/load_references_b.py
rename to examples/output/load_references_b.py
diff --git a/examples/process_cs101.py b/examples/process_cs101.py
new file mode 100644
index 0000000000000000000000000000000000000000..0268b192b2690b67385f69724140a42e962becd3
--- /dev/null
+++ b/examples/process_cs101.py
@@ -0,0 +1,25 @@
+def main():
+    from snipper.snip_dir import snip_dir
+    from snipper.load_citations import get_aux, get_bibtex
+    bibfile = get_bibtex('latex/library.bib')
+    auxfile = get_aux('latex/index.aux')
+
+    references = dict(bibtex=bibfile,
+                      aux=auxfile,
+                      commands=[
+                          dict(command='\\aref2', output="(Assignment 2, \\ref{%s})", aux=auxfile),
+                          dict(command='\\href', output="\cite[\\ref{%s}]{herlau}", aux=auxfile),
+                        ]
+                      )
+
+
+    output = snip_dir(source_dir="./cs101_instructorm", dest_dir="./cs101_students", output_dir="./cs101_output",
+             references=references,
+             )
+
+    a = 234
+
+
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/src/codesnipper.egg-info/PKG-INFO b/src/codesnipper.egg-info/PKG-INFO
index 487bc49d1a1da8cad30d409ce4d898b017babc34..070de1a0d19a7a2a6bbd234536ec5b69ef17e895 100644
--- a/src/codesnipper.egg-info/PKG-INFO
+++ b/src/codesnipper.egg-info/PKG-INFO
@@ -1,11 +1,11 @@
 Metadata-Version: 2.1
 Name: codesnipper
-Version: 0.0.1
+Version: 0.0.2
 Summary: A lightweight framework for censoring student solutions files and extracting code + output
 Home-page: https://lab.compute.dtu.dk/tuhe/snipper
 Author: Tue Herlau
 Author-email: tuhe@dtu.dk
-License: UNKNOWN
+License: MIT
 Project-URL: Bug Tracker, https://lab.compute.dtu.dk/tuhe/snipper/issues
 Platform: UNKNOWN
 Classifier: Programming Language :: Python :: 3
@@ -15,8 +15,158 @@ Requires-Python: >=3.8
 Description-Content-Type: text/markdown
 License-File: LICENSE
 
-# Snip
-A lightweight framework for removing code from student solutions. Currently used at DTU. 
+# Snipper
+A lightweight framework for removing code from student solutions.
+## Installation
+```console
+pip install codesnipper
+```
+## What it does
+This project address the following three challenges for administering a python-based course
 
+ - You need to maintain a (working) version for debugging as well as a version handed out to students (with code missing)
+ - You ideally want to make references in source code to course material *"(see equation 2.1 in exercise 5)"* but these tend to go out of date
+ - You want to include code snippets and code output in lectures notes/exercises/beamer slides
+ - You want to automatically create student solutions
 
+This framework address these problems and allow you to maintain a **single**, working project repository. 
+
+The project is currently used in **02465** at DTU. An example of student code can be found at:
+ - https://gitlab.gbar.dtu.dk/02465material/02465students/blob/master/irlc/ex02/dp.py
+
+A set of lectures notes where all code examples/output are automatically generated from the working repository can be found a
+- https://lab.compute.dtu.dk/tuhe/books (see **Sequential decision making**)
+ 
+## How it works
+The basic functionality is quite simple. You start with your working script in your private repository and add special tags to the script. 
+In this case I have added the tags `#!b` (cut a block) and `#!f` (cut function scope). 
+```python
+def myfun(): #!f The error I am going to raise
+    """ The function docstring will not be removed"""
+    print("This is a function")
+    return 42
+    
+def a_long_function():
+    a = 234
+    print("a line")
+    print("a line") #!b
+    print("a line")
+    print("a line") #!b Insert three missing print statements. 
+    print("a line")
+    return a
+    
+if __name__ == "__main__":
+    myfun()
+```
+This will produce the following file:
+```python
+def myfun():
+    """ The function docstring will not be removed"""
+    # TODO: 2 lines missing.
+    raise NotImplementedError("The error I am going to raise")
+    
+def a_long_function():
+    a = 234
+    print("a line")
+    # TODO: 3 lines missing.
+    raise NotImplementedError("Insert three missing print statements.")
+    print("a line")
+    return a
+    
+if __name__ == "__main__":
+    myfun()
+```
+You can also use the framework to capture code snippets, outputs and interactive python output. 
+To do this, save the following in `foo.py`
+```python
+def myfun(): #!s This snippet will be saved to foo.py in the output directory. 
+    print("Hello") #!s 
+
+print("Do not capture me") 
+for i in range(4): #!o
+    print("Output", i)
+print("Goodbuy world") #!o
+print("don't capture me")
+
+# Interactive pythong example
+print("Hello World") #!i #!i # this is a single-line cutout.
+````
+These block-tags will create a file `foo.py` (in the output directory) containing
+```python
+def myfun():
+    print("Hello") 
+```
+A file `foo.txt` containing the captured output
+```txt
+Output 0
+Output 1
+Output 2
+Output 3
+Goodbuy world
+```
+and a typeset version of an interactive python session in `foo.pyi` (use `pycon` in minted; this gitlab server appears not to support `pycon`)
+```pycon
+>>> print("hello world")
+Hello World"
+```
+All these files can be directly imported into `LaTeX` using e.g. `minted`: You never need to mix `LaTeX` code and python again!
+
+
+## References: 
+Bibliography references can be loaded from `references.bib`-files and in-document references from the `.aux` file. 
+For this example, we will insert references shown in the `examples/latex/index.tex`-document. To do so, we can use these tags:
+```python
+def myfun(): #!s
+    """
+    To solve this exercise, look at \ref{eq1} in \ref{sec1}.
+    You can also look at \cite{bertsekasII} and \cite{herlau}
+    More specifically, look at \cite[Equation 117]{bertsekasII} and \cite[\ref{fig1}]{herlau}
+
+    We can also write a special tag to reduce repetition: \nref{fig1} and \nref{sec1}.
+    """
+    return 42 #!s
+
+```
+We can manually compile this example by first loading the aux-files and the bibliographies as follows:
+```python 
+# load_references.py
+    from snipper.citations import get_bibtex, get_aux 
+    bibfile = "latex/library.bib"
+    auxfile = 'latex/index.aux'
+    bibtex = get_bibtex(bibfile)
+    aux = get_aux(auxfile) 
+```
+Next, we load the python file containing the reference code and fix all references based on the aux and bibliography data. 
+```python 
+# load_references.py
+    file = "citations.py" 
+    with open(file, 'r') as f:
+        lines = f.read().splitlines()
+    lines = fix_aux(lines, aux=aux)
+    lines = fix_aux_special(lines, aux=aux, command='\\nref', bibref='herlau')
+    lines = fix_bibtex(lines, bibtex=bibtex)
+    with open('output/citations.py', 'w') as f:
+        f.write("\n".join(lines)) 
+```
+The middle command is a convenience feature: It allows us to specify a special citation command `\nref{..}` which always compiles to `\cite[\ref{...}]{herlau}`. This is useful if e.g. `herlau` is the bibtex key for your lecture notes. The result is as follows:
+```python 
+"""
+References:
+  [Ber07] Dimitri P. Bertsekas. Dynamic Programming and Optimal Control, Vol. II. Athena Scientific, 3rd edition, 2007. ISBN 1886529302.
+  [Her21] Tue Herlau. Sequential decision making. (See 02465_Notes.pdf), 2021.
+"""
+def myfun(): #!s
+    """
+    To solve this exercise, look at eq. (1) in Section 1.
+    You can also look at (Ber07) and (Her21)
+    More specifically, look at (Ber07, Equation 117) and (Her21, Figure 1)
+
+    We can also write a special tag to reduce repetition: (Her21, Figure 1) and (Her21, Section 1).
+    """
+    return 42 #!s
+```
+Note this example uses the low-level api. Normally you would just pass the bibtex and aux-file to the main censor-file command.
+
+## Additional features:
+-  You can name tags using `#!s=bar` to get a `foo_bar.py` snippet. This is useful when you need to cut multiple sessions. This also works for the other tags. 
 
diff --git a/src/codesnipper.egg-info/SOURCES.txt b/src/codesnipper.egg-info/SOURCES.txt
index 8b9ec963f866926c6443dfdfdce0f0fd577af5ee..0064f64f775fd70dcb7864eb21a94d63b9516fe6 100644
--- a/src/codesnipper.egg-info/SOURCES.txt
+++ b/src/codesnipper.egg-info/SOURCES.txt
@@ -9,5 +9,8 @@ src/codesnipper.egg-info/dependency_links.txt
 src/codesnipper.egg-info/requires.txt
 src/codesnipper.egg-info/top_level.txt
 src/snipper/__init__.py
+src/snipper/citations.py
+src/snipper/fix_cite.py
+src/snipper/fix_s.py
 src/snipper/snip_dir.py
-src/snipper/snipper.py
\ No newline at end of file
+src/snipper/snipper_main.py
\ No newline at end of file
diff --git a/src/codesnipper.egg-info/requires.txt b/src/codesnipper.egg-info/requires.txt
index 7f7afbf3bf54b346092be6a72070fcbd305ead1e..4ba7d8fcd57f03899543a71a0d303d13cb5072a3 100644
--- a/src/codesnipper.egg-info/requires.txt
+++ b/src/codesnipper.egg-info/requires.txt
@@ -1 +1,3 @@
-jinja2
+pexpect
+wexpect
+pybtex
diff --git a/src/snipper/__pycache__/citations.cpython-38.pyc b/src/snipper/__pycache__/citations.cpython-38.pyc
deleted file mode 100644
index 9a647f415713b55177e96ceff6c8c791938806ce..0000000000000000000000000000000000000000
Binary files a/src/snipper/__pycache__/citations.cpython-38.pyc and /dev/null differ
diff --git a/src/snipper/__pycache__/fix_s.cpython-38.pyc b/src/snipper/__pycache__/fix_s.cpython-38.pyc
index a4e3cf8604fe8f78dcdcc41bf88f448690de8363..b161b2c89e36fa9e9f5a4b97990c7c3ba4833196 100644
Binary files a/src/snipper/__pycache__/fix_s.cpython-38.pyc and b/src/snipper/__pycache__/fix_s.cpython-38.pyc differ
diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b1009c05c3266267f825bf7bb5d760fd4fd83f6
--- /dev/null
+++ b/src/snipper/block_parsing.py
@@ -0,0 +1,78 @@
+def f2(lines, tag, i=0, j=0):
+    for k in range(i, len(lines)):
+        index = lines[k].find(tag, j if k == i else 0)
+        if index >= 0:
+            return k, index
+    return None, None
+
+
+def block_iterate(lines, tag):
+    contents = {'joined': lines}
+    while True:
+        contents = block_split(contents['joined'], tag)
+        if contents is None:
+            break
+
+        yield  contents
+
+
+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):
+        k = line.find(" ")
+        tag_args = (line[:k + 1] if k >= 0 else line)[len(tag):]
+        if len(tag_args) == 0:
+            return {'': ''}  # No name.
+        tag_args = dict([t.split("=") for t in tag_args.split(";")])
+        return tag_args
+
+    if i is None:
+        return None
+    else:
+        start_tag_args = get_tag_args(lines[i][j:])
+        START_TAG = f"{tag}={start_tag_args['']}" if '' in start_tag_args else tag
+        END_TAG = START_TAG
+        i2, j2 = f2(lines, END_TAG, i=i, j=j+1)
+        if i2 == None:
+            END_TAG = tag
+            i2, j2 = f2(lines, END_TAG, i=i, j=j+1)
+        if i2 == None:
+            print("\n".join( lines[i:]))
+            raise Exception("Did not find matching tag", tag)
+
+
+    if i == i2:
+        # Splitting a single line. To reduce confusion, this will be treated slightly differently:
+        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)
+        return c2
+    else:
+        contents['first'] = lines[:i]
+        contents['last'] = lines[i2+1:]
+
+        def argpost(line, j):
+            nx_tag = line.find(stag, j+1)
+            arg1 = line[j+len(tag):nx_tag]
+            if nx_tag >= 0:
+                post = line[nx_tag:]
+            else:
+                post = ''
+            return arg1, post
+
+        contents['arg1'], contents['post1'] = argpost(lines[i], j)
+        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['start_tag_args'] = start_tag_args
+        contents['name'] = start_tag_args['']
+        return contents
\ No newline at end of file
diff --git a/src/snipper/fix_bf.py b/src/snipper/fix_bf.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/snipper/fix_cite.py b/src/snipper/fix_cite.py
index 475e44d5f36f80dd441574fc757cd6dec32c8b18..7fbc1b44a4728216c0c5bdad52d2ace5d239a0e5 100644
--- a/src/snipper/fix_cite.py
+++ b/src/snipper/fix_cite.py
@@ -1,34 +1,10 @@
-from snipper.citations import find_tex_cite
+from snipper.load_citations import find_tex_cite
 from snipper.snipper_main import COMMENT
 
-def fix_references(lines, info, strict=True):
-    for cmd in info['new_references']:
-        lines = fix_single_reference(lines, cmd, info['new_references'][cmd], strict=strict)
-    return lines
 
-def fix_single_reference(lines, cmd, aux, strict=True):
-    references = aux
-    s = "\n".join(lines)
-    i = 0
-    while True:
-        (i, j), reference, txt = find_tex_cite(s, start=i, key=cmd)
-        if i < 0:
-            break
-        if reference not in references:
-            er = "cref label not found for label: " + reference
-            if strict:
-                raise IndexError(er)
-            else:
-                print(er)
-                continue
-        r = references[reference]
-        rtxt = r['nicelabel']
-        s = s[:i] + rtxt + s[j + 1:]
-        i = i + len(rtxt)
-        print(cmd, rtxt)
-
-    lines = s.splitlines(keepends=False)
-    return lines
+def fix_citations():
+    # This should be the master function.
+    pass
 
 
 def fix_aux_special(lines, aux, command='\\nref', bibref='herlau'):
@@ -70,4 +46,35 @@ def fix_bibtex(lines, bibtex):
         s = s[:i] + "References:\n" + "\n".join(all_refs) +"\n"+ s[i:]
 
     # s = s.replace(cpr, info['code_copyright'])
-    return s.splitlines()
\ No newline at end of file
+    return s.splitlines()
+
+
+
+def fix_references(lines, info, strict=True):
+    for cmd in info['new_references']:
+        lines = fix_single_reference(lines, cmd, info['new_references'][cmd], strict=strict)
+    return lines
+
+def fix_single_reference(lines, cmd, aux, strict=True):
+    references = aux
+    s = "\n".join(lines)
+    i = 0
+    while True:
+        (i, j), reference, txt = find_tex_cite(s, start=i, key=cmd)
+        if i < 0:
+            break
+        if reference not in references:
+            er = "cref label not found for label: " + reference
+            if strict:
+                raise IndexError(er)
+            else:
+                print(er)
+                continue
+        r = references[reference]
+        rtxt = r['nicelabel']
+        s = s[:i] + rtxt + s[j + 1:]
+        i = i + len(rtxt)
+        print(cmd, rtxt)
+
+    lines = s.splitlines(keepends=False)
+    return lines
\ No newline at end of file
diff --git a/src/snipper/fix_s.py b/src/snipper/fix_s.py
index 0eed3c8ea9e9b15926a425aad73adc5aa60fcde5..b8f1d9b35abf9166bc0b194d8e59be6aa9068f19 100644
--- a/src/snipper/fix_s.py
+++ b/src/snipper/fix_s.py
@@ -1,79 +1,6 @@
-import functools
 from collections import defaultdict
-from snipper.snipper_main import full_strip, block_process
-
-def f2(lines, tag, i=0, j=0):
-    for k in range(i, len(lines)):
-        index = lines[k].find(tag, j if k == i else 0)
-        if index >= 0:
-            return k, index
-    return None, None
-
-def block_iterate(lines, tag):
-    contents = {'joined': lines}
-    while True:
-        contents = block_split(contents['joined'], tag)
-        if contents is None:
-            break
-
-        yield  contents
-
-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):
-        k = line.find(" ")
-        tag_args = (line[:k + 1] if k >= 0 else line)[len(tag):]
-        if len(tag_args) == 0:
-            return {'': ''}  # No name.
-        tag_args = dict([t.split("=") for t in tag_args.split(";")])
-        return tag_args
-
-    if i is None:
-        return None
-    else:
-        start_tag_args = get_tag_args(lines[i][j:])
-        START_TAG = f"{tag}={start_tag_args['']}" if '' in start_tag_args else tag
-        END_TAG = START_TAG
-        i2, j2 = f2(lines, END_TAG, i=i, j=j+1)
-        if i2 == None:
-            END_TAG = tag
-            i2, j2 = f2(lines, END_TAG, i=i, j=j+1)
-
-    if i == i2:
-        # Splitting a single line. To reduce confusion, this will be treated slightly differently:
-        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)
-        return c2
-    else:
-        contents['first'] = lines[:i]
-        contents['last'] = lines[i2+1:]
-
-        def argpost(line, j):
-            nx_tag = line.find(stag, j+1)
-            arg1 = line[j+len(tag):nx_tag]
-            if nx_tag >= 0:
-                post = line[nx_tag:]
-            else:
-                post = ''
-            return arg1, post
-
-        contents['arg1'], contents['post1'] = argpost(lines[i], j)
-        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['start_tag_args'] = start_tag_args
-        contents['name'] = start_tag_args['']
-        return contents
+import os
+from snipper.block_parsing import block_iterate
 
 
 def get_s(lines):
@@ -86,8 +13,6 @@ def get_s(lines):
         output[name] = [l for c in co for l in c['block']]
     return output
 
-import os
-
 def save_s(lines, output_dir, file_path): # save file snips to disk
     content = get_s(lines)
     if not os.path.isdir(output_dir):
@@ -99,30 +24,6 @@ def save_s(lines, output_dir, file_path): # save file snips to disk
         with open(output_dir + "/" + os.path.basename(file_path)[:-3] + ("_" + name if len(name) > 0 else name) + ".py", 'w') as f:
             f.write(out)
 
-        # out = "\n".join([f"# {include_path_base}"] + ["\n".join(v[1]) for v in c if v[0] == outf])
-        # with open(outf, 'w') as f:
-        #     f.write(out)
-
-    # def block_fun(lines, start_extra, end_extra, art, output, **kwargs):
-    #     outf = output + ("_" + art if art is not None and len(art) > 0 else "") + ".py"
-    #     lines = full_strip(lines)
-    #     return lines, [outf, lines]
-    # try:
-    #     a,b,c,_ = block_process(lines, tag="#!s", block_fun=functools.partial(block_fun, output=output))
-    #     if len(c)>0:
-    #         kvs= { v[0] for v in c}
-    #         for outf in kvs:
-    #             out = "\n".join([f"# {include_path_base}"]  + ["\n".join(v[1]) for v in c if v[0] == outf] )
-    #             with open(outf, 'w') as f:
-    #                 f.write(out)
-    #
-    # except Exception as e:
-    #     print("lines are")
-    #     print("\n".join(lines))
-    #     print("Bad thing in #!s command in file", file)
-    #     raise e
-    # return lines
-
 s1 = """
 L1
 L2 #!s=a
@@ -147,5 +48,3 @@ if __name__ == "__main__":
     # contents = block_split(s1.splitlines(), tag="#!s")
     # contents = block_split(contents['joined'], tag="#!s")
     # lines2 = contents['first'] +
-    a = 234
-    pass
\ No newline at end of file
diff --git a/src/snipper/citations.py b/src/snipper/load_citations.py
similarity index 100%
rename from src/snipper/citations.py
rename to src/snipper/load_citations.py
diff --git a/src/snipper/snip_dir.py b/src/snipper/snip_dir.py
index 0a68a9d22b51eda7d2755f0effb11c3b89268d94..15657afecaa9afa3748157dd02c6b70c4687de0d 100644
--- a/src/snipper/snip_dir.py
+++ b/src/snipper/snip_dir.py
@@ -5,7 +5,15 @@ import time
 import fnmatch
 
 
-def snip_dir(source_dir, dest_dir, exclude=None, clean_destination_dir=True):
+def snip_dir(source_dir,  # Sources
+             dest_dir,  # 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):
+
+    if references == None:
+        references = dict(aux=None, bibtex=None,  commands=[])
+
     if exclude == None:
         exclude = []
     if not os.path.exists(dest_dir):
diff --git a/src/snipper/snipper_main.py b/src/snipper/snipper_main.py
index 188603358c62a1b0c274b2c9a28d6e4901c1f768..dc6bbdcbdd5ccda5e590f0eb49938ec3192b8898 100644
--- a/src/snipper/snipper_main.py
+++ b/src/snipper/snipper_main.py
@@ -315,7 +315,6 @@ def censor_code(lines, keep=True):
 
 
 
-
 def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files=True, solution_list=None,
                 censor_files=True,
                 include_path_base=None,