diff --git a/README.md b/README.md
index 4541d8897c386d563d61ae30594a1a62b8d506c8..5a4acc3bf9bcf987ef9ec60350bab6a8c9d6b3e4 100644
--- a/README.md
+++ b/README.md
@@ -20,82 +20,205 @@ The project is currently used in **02465** at DTU. An example of student code ca
 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). 
+# Usage
+All examples can be found in the `/examples` directory. The idea is all our (complete) files are found in the instructor directory and snipper keeps everything up-to-date:
+```text
+examples/cs101_instructor  # This directory contains the (hidden) instructor files. You edit these
+examples/cs101_students    # This directory contains the (processed) student files. Don't edit these
+examples/cs101_output      # This contains automatically generated contents (snippets, etc.).
+```
+The basic functionality is you insert special comment tags in your source, such as `#!b` or `#!s` and the script then process
+the sources based on the tags.  The following will show most basic usages:
+
+## The #f!-tag
+Let's start with the simplest example, blocking out a function (see `examples/cs101_instructor/f_tag.py`; actually it will work for any scope)
+You insert a comment like: `#!f <exception message>` like so:
 ```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()
+def myfun(a,b): #!f return the sum of a and b
+    """ The doc-string is not removed. """
+    sm = a+b
+    return sm
 ```
-This will produce the following file:
+To compile this (and all other examples) use the script `examples/process_cs101.py`
 ```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()
+    from snipper.snip_dir import snip_dir
+    snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output")
+```
+The output can be found in `examples/students/f_tag.py`. It will cut out the body of the function but leave any return statement and docstrings. It will also raise an exception (and print how many lines are missing) to help students.
+```python
+"""
+"""
+def myfun(a,b): 
+    """ The doc-string is not removed. """
+    # TODO: 1 lines missing.
+    raise NotImplementedError("return the sum of a and b")
+    return sm
 ```
-You can also use the framework to capture code snippets, outputs and interactive python output. 
-To do this, save the following in `foo.py`
+
+## The #b!-tag
+The #!b-tag allows you more control over what is cut out. The instructor file:
 ```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
+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) #!b Compute the list `primes` here of all primes up to `limit`
+    return primes
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:")
+print(width*height) #!b #!b Compute and print area here
+print("and that is a fact!")
+```
+Is compiled into:
+```python 
+"""
+"""
+def primes_sieve(limit):
+    # TODO: 8 lines missing.
+    raise NotImplementedError("Compute the list `primes` here of all primes up to `limit`")
+    return primes
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:")
+# TODO: 1 lines missing.
+raise NotImplementedError("Compute and print area here")
+print("and that is a fact!")
+```
+This allows you to cut out text across scopes, but still allows you to insert exceptions. 
+
+
+
+## The #s!-tag
+The #!s-tag is useful for making examples to include in exercises and lecture notes. The #!s (snip) tag cuts out the text between 
+tags and places it in files found in the output-directory. As an example, here is the instructor file:
 ```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`)
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:") #!s
+print(width*height)  #!s  # This is an example of a simple cutout
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+def primes_sieve(limit): #!s=a # A named cutout
+    limitn = limit+1
+    primes = range(2, limitn)
+    for i in primes: #!s=b A nested/named cutout.
+        factors = list(range(i, limitn, i))
+        for f in factors[1:]:
+            if f in primes:
+                primes.remove(f)  #!s=b
+    return primes #!s=a
+```
+Note it allows 
+ - naming using the #!s=<name> command 
+ - automatically join snippets with the same name (useful to cut out details)
+ - The named tags will be matched, and do not have to strictly contained in each other
+
+This example will produce three files
+`cs101_output/s_tag.py`, `cs101_output/s_tag_a.py`, and `cs101_output/s_tag_b.py` containing the output:
+```python 
+# s_tag.py
+print("Area of square of width", width, "and height", height, "is:") 
+print(width*height)  #!s  # This is an example of a simple cutout
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+def primes_sieve(limit): 
+print(width*height)  
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+def primes_sieve(limit): 
+    limitn = limit+1
+    primes = range(2, limitn)
+    for i in primes: 
+print("An extra cutout") 
+def primes_sieve(limit): 
+    limitn = limit+1
+    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)  
+print("An extra cutout") 
+def primes_sieve(limit): 
+    limitn = limit+1
+    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
+```
+and 
+```python 
+
+```
+and finally:
+```python 
+
+```
+I recommend using `\inputminted{filename}` to insert the cutouts in LaTeX. 
+
+
+## The #o!-tag
+The #!o-tag allows you to capture output from the code, which can be useful when showing students the expected 
+behavior of their scripts. Like the #!s-tag, the #!o-tags can be named. 
+
+As an example, Consider the instructor file
+```python
+if __name__ == "__main__":
+    print("Here are the first 4 square numbers") #!o=a
+    for k in range(1,5):
+        print(k*k, "is a square")
+    #!o=a
+    print("This line will not be part of a cutout.")
+    width, height = 2, 4 #!o=b
+    print("Area of square of width", width, "and height", height, "is:")
+    print(width*height)
+    print("and that is a fact!") #!o=b
+```
+This example will produce two files `cs101_output/o_tag_a.txt`, `cs101_output/o_tag_b.txt`:
+```python 
+
+```
+and 
+```python 
+
+```
+
+## The #i!-tag
+The #!i-tag allows you to create interactive python shell-snippets that can be imported using the minted `pycon` environment ('\inputminted{python}{input.shell}').
+ As an example, consider the instructor file
+```python
+for animal in ["Dog", "cat", "wolf"]: #!i=a
+    print("An example of a four legged animal is", animal) #!i=a
+#!i=b
+def myfun(a,b):
+    return a+b
+myfun(3,4) #!i=b
+# Snipper will automatically insert an 'enter' after the function definition.
+```
+This example will produce two files `cs101_output/i_tag_a.shell`, `cs101_output/i_tag_b.shell`:
 ```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!
+and 
+```pycon 
+
+```
+Note that apparently there 
+ is no library for converting python code to shell sessions so I had to write it myself, which means it can properly get confused with multi-line statements (lists, etc.). On the plus-side, it will automatically insert newlines after the end of scopes. 
+ My parse is also known to be a bit confused if your code outputs `...` since it has to manually parse the interactive python session and this normally indicates a new line. 
+
+## References and citations (`\ref` and `\cite`)
+One of the most annoying parts of maintaining student code is to constantly write "see equation on slide 41 bottom" only to have the reference go stale because slide 23 got removed. Well now anymore, now you can direcly refence anything with a bibtex or aux file!
+Let's consider the following example of a simple document with a couple of references: (see `examples/latex/index.pdf`):
+
+![LaTeX sample](https://gitlab.compute.dtu.dk/tuhe/snipper/-/blob/maindocs/index.png)
 
 
-## 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
@@ -112,39 +235,20 @@ def myfun(): #!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).
+    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
 ```
diff --git a/docs/README.jinja.md b/docs/README.jinja.md
index add4775f6a54e5c05c09e6fa5b63c3b9a956a627..28e0cd27231758708816341e3feefaa2a11009bd 100644
--- a/docs/README.jinja.md
+++ b/docs/README.jinja.md
@@ -20,82 +20,113 @@ The project is currently used in **02465** at DTU. An example of student code ca
 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). 
+# Usage
+All examples can be found in the `/examples` directory. The idea is all our (complete) files are found in the instructor directory and snipper keeps everything up-to-date:
+```text
+examples/cs101_instructor  # This directory contains the (hidden) instructor files. You edit these
+examples/cs101_students    # This directory contains the (processed) student files. Don't edit these
+examples/cs101_output      # This contains automatically generated contents (snippets, etc.).
+```
+The basic functionality is you insert special comment tags in your source, such as `#!b` or `#!s` and the script then process
+the sources based on the tags.  The following will show most basic usages:
+
+## The #f!-tag
+Let's start with the simplest example, blocking out a function (see `examples/cs101_instructor/f_tag.py`; actually it will work for any scope)
+You insert a comment like: `#!f <exception message>` like so:
+```python
+{{ cs101_instructor.f_tag_py }}
+```
+To compile this (and all other examples) use the script `examples/process_cs101.py`
+```python
+{{ process_cs101_py }}
+```
+The output can be found in `examples/students/f_tag.py`. It will cut out the body of the function but leave any return statement and docstrings. It will also raise an exception (and print how many lines are missing) to help students.
+```python
+{{ cs101_students.f_tag_py }}
+```
+
+## The #b!-tag
+The #!b-tag allows you more control over what is cut out. The instructor file:
 ```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:
+{{ cs101_instructor.b_tag_py }}
+```
+Is compiled into:
+```python 
+{{ cs101_students.b_tag_py }}
+```
+This allows you to cut out text across scopes, but still allows you to insert exceptions. 
+
+
+
+## The #s!-tag
+The #!s-tag is useful for making examples to include in exercises and lecture notes. The #!s (snip) tag cuts out the text between 
+tags and places it in files found in the output-directory. As an example, here is the instructor 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`
+{{ cs101_instructor.s_tag_py }}
+```
+Note it allows 
+ - naming using the #!s=<name> command 
+ - automatically join snippets with the same name (useful to cut out details)
+ - The named tags will be matched, and do not have to strictly contained in each other
+
+This example will produce three files
+`cs101_output/s_tag.py`, `cs101_output/s_tag_a.py`, and `cs101_output/s_tag_b.py` containing the output:
+```python 
+{{ cs101_output.s_tag_py }}
+```
+and 
+```python 
+{{ cs101_output.s_tag_a_py }}
+```
+and finally:
+```python 
+{{ cs101_output.s_tag_b_py }}
+```
+I recommend using `\inputminted{filename}` to insert the cutouts in LaTeX. 
+
+
+## The #o!-tag
+The #!o-tag allows you to capture output from the code, which can be useful when showing students the expected 
+behavior of their scripts. Like the #!s-tag, the #!o-tags can be named. 
+
+As an example, Consider the instructor file
 ```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
+{{ cs101_instructor.o_tag_py }}
+```
+This example will produce two files `cs101_output/o_tag_a.txt`, `cs101_output/o_tag_b.txt`:
+```python 
+{{ cs101_output.o_tag_a_py }}
+```
+and 
+```python 
+{{ cs101_output.o_tag_b_py }}
+```
+
+## The #i!-tag
+The #!i-tag allows you to create interactive python shell-snippets that can be imported using the minted `pycon` environment ('\inputminted{python}{input.shell}').
+ As an example, consider the instructor file
 ```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`)
+{{ cs101_instructor.i_tag_py }}
+```
+This example will produce two files `cs101_output/i_tag_a.shell`, `cs101_output/i_tag_b.shell`:
 ```pycon
->>> print("hello world")
-Hello World"
+{{ cs101_output.i_tag_a_py }}
+```
+and 
+```pycon 
+{{ cs101_output.i_tag_b_py }}
 ```
-All these files can be directly imported into `LaTeX` using e.g. `minted`: You never need to mix `LaTeX` code and python again!
+Note that apparently there 
+ is no library for converting python code to shell sessions so I had to write it myself, which means it can properly get confused with multi-line statements (lists, etc.). On the plus-side, it will automatically insert newlines after the end of scopes. 
+ My parse is also known to be a bit confused if your code outputs `...` since it has to manually parse the interactive python session and this normally indicates a new line. 
+
+## References and citations (`\ref` and `\cite`)
+One of the most annoying parts of maintaining student code is to constantly write "see equation on slide 41 bottom" only to have the reference go stale because slide 23 got removed. Well now anymore, now you can direcly refence anything with a bibtex or aux file!
+Let's consider the following example of a simple document with a couple of references: (see `examples/latex/index.pdf`):
+
+![LaTeX sample]({{resources}}docs/index.png)
 
 
-## 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
diff --git a/docs/build_docs.py b/docs/build_docs.py
index 46e7a45ce1114a3e4007293330f919b5f36d400e..35d26b81f01163799c4fabdaf406b971d025eaed 100644
--- a/docs/build_docs.py
+++ b/docs/build_docs.py
@@ -19,47 +19,47 @@ def my_nup(path):
 if __name__ == "__main__":
     from snipper.fix_s import save_s
     from snipper.snipper_main import censor_file
-
+    data = {}
     EX_BASE = "../examples"
     np = EX_BASE + "/latex"
+    import glob
+
+    dirs = ['cs101_output', 'cs101_instructor', 'cs101_students']
+    def dir_import(dir):
+        dat = {}
+        for f in glob.glob(dir+"/*.*"):
+            name = os.path.basename(f).replace(".", '_')
+            with open(f, 'r') as f:
+                s = [s for s in f.read().rstrip().splitlines() if s.strip() != '']
+                dat[name] = '\n'.join(s)
+        return dat
+
+    for d in dirs:
+        data[d] = dir_import("../examples/"+d)
+        # for f in glob.glob("../examples/"+d+"/*.*"):
+        #     name = os.path.basename(f).replace(".",'_')
+        #     with open(f, 'r') as f:
+        #         data[d][name] = f.read()
+
+    data = {**data, **dir_import('../examples')}
+    data['resources'] = 'https://gitlab.compute.dtu.dk/tuhe/snipper/-/blob/main'
 
-    # if os.path.isdir(np):
-    #     shutil.rmtree(np)
-    # os.makedirs(np)
-    # slider_cli(latexfile=f"{np}/index.tex", force=True)
-    # np_basic1 = EX_BASE + "/basic1"
-    # if os.path.isdir(np_basic1):
-    #     shutil.rmtree(np_basic1)
-    # shutil.copytree(np, np_basic1)
-    # shutil.copyfile("myoverlay.svg", np_basic1 +"/osvgs/myoverlay.svg")
-    # slider_cli(latexfile=f"{np_basic1}/index.tex")
-    #
-    #
-    # my_nup(np + "/index.pdf")
-    # my_nup(f"{np_basic1}/index.pdf")
-    #
     convert.pdf2png(np + "/index.pdf", "./index.png", scale_to=600)
-    # output = np +"/index_2up.pdf"
-    #
-    # data = {}
-    # data['resources'] = 'https://gitlab.compute.dtu.dk/tuhe/slider/-/raw/main'
-    # with open(np + "/index.tex", 'r') as f:
-    #     data['basic0_tex'] = f.read()
 
-    data = {}
     # Build the docs.
     import glob
-    with open("../example/citations.py", 'r') as f:
+    with open("../examples/citations.py", 'r') as f:
         data['citations_orig_py'] = f.read()
-    for file in glob.glob("../example/output/*.*"):
-        with open(file, 'r') as f:
-            data[os.path.basename(file).replace(".", "_")] = f.read()
+    # for file in glob.glob("../examples/output/*.*"):
+    #     with open(file, 'r') as f:
+    #         data[os.path.basename(file).replace(".", "_")] = f.read()
 
     with open("README.jinja.md", 'r') as f:
         s = jinja2.Environment(loader=jinja2.FileSystemLoader([".", "../example"])).from_string(f.read()).render(data)
-    with open("../README.md",'w') as f:
+    with open("../README.md", 'w') as f:
         f.write(s)
 
 
 
+
     pass
\ No newline at end of file
diff --git a/docs/index.png b/docs/index.png
new file mode 100644
index 0000000000000000000000000000000000000000..53392ac5e0855f43a4b1ac49f61f6b5dad857278
Binary files /dev/null and b/docs/index.png differ
diff --git a/examples/cs101_instructor/b_tag.py b/examples/cs101_instructor/b_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f8be07667884c5e5b3b37c7033210641c4b8f33
--- /dev/null
+++ b/examples/cs101_instructor/b_tag.py
@@ -0,0 +1,15 @@
+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) #!b Compute the list `primes` here of all primes up to `limit`
+    return primes
+
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:")
+print(width*height) #!b #!b Compute and print area here
+print("and that is a fact!")
diff --git a/examples/cs101_instructor/f_tag.py b/examples/cs101_instructor/f_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..354421ab30f28d7ee8d9948a3dc2a8ce895f5d22
--- /dev/null
+++ b/examples/cs101_instructor/f_tag.py
@@ -0,0 +1,6 @@
+
+def myfun(a,b): #!f return the sum of a and b
+    """ The doc-string is not removed. """
+    sm = a+b
+    return sm
+
diff --git a/examples/cs101_instructor/i_tag.py b/examples/cs101_instructor/i_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..111237d79ac6eda7e2779ee54622b2d65952a814
--- /dev/null
+++ b/examples/cs101_instructor/i_tag.py
@@ -0,0 +1,8 @@
+for animal in ["Dog", "cat", "wolf"]: #!i=a
+    print("An example of a four legged animal is", animal) #!i=a
+
+#!i=b
+def myfun(a,b):
+    return a+b
+myfun(3,4) #!i=b
+# Snipper will automatically insert an 'enter' after the function definition.
diff --git a/examples/cs101_instructor/o_tag.py b/examples/cs101_instructor/o_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa153704b554c8d1b512884ec230a57a3741eb50
--- /dev/null
+++ b/examples/cs101_instructor/o_tag.py
@@ -0,0 +1,11 @@
+if __name__ == "__main__":
+    print("Here are the first 4 square numbers") #!o=a
+    for k in range(1,5):
+        print(k*k, "is a square")
+    #!o=a
+    print("This line will not be part of a cutout.")
+    width, height = 2, 4 #!o=b
+    print("Area of square of width", width, "and height", height, "is:")
+    print(width*height)
+    print("and that is a fact!") #!o=b
+
diff --git a/examples/cs101_instructor/homework1.py b/examples/cs101_instructor/references.py
similarity index 62%
rename from examples/cs101_instructor/homework1.py
rename to examples/cs101_instructor/references.py
index 49ed989a2f83b491f310646921c0cf224eb99503..7c6a5901389983029351b52f2fad036e1d489fe3 100644
--- a/examples/cs101_instructor/homework1.py
+++ b/examples/cs101_instructor/references.py
@@ -1,4 +1,4 @@
-def myfun(): #!s
+def myfun():
     """
     Simple aux references \ref{eq1} in \ref{sec1}.
     Simple bibtex citations: \cite{bertsekasII} and \cite[Somewhere around the middle]{herlau}
@@ -9,12 +9,6 @@ def myfun(): #!s
     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
+    print("See \ref{sec1}")  # Also works.
+    return 42
 
diff --git a/examples/cs101_instructor/s_tag.py b/examples/cs101_instructor/s_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..03a870626ee78ec7ddbccb5b93358498d959dc69
--- /dev/null
+++ b/examples/cs101_instructor/s_tag.py
@@ -0,0 +1,17 @@
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:") #!s
+print(width*height)  #!s  # This is an example of a simple cutout
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+
+def primes_sieve(limit): #!s=a # A named cutout
+    limitn = limit+1
+    primes = range(2, limitn)
+
+    for i in primes: #!s=b A nested/named cutout.
+        factors = list(range(i, limitn, i))
+        for f in factors[1:]:
+            if f in primes:
+                primes.remove(f)  #!s=b
+    return primes #!s=a
+
diff --git a/examples/cs101_output/homework1.py b/examples/cs101_output/homework1.py
new file mode 100644
index 0000000000000000000000000000000000000000..2305ed7c90c1200b432df4d4510b017a46073416
--- /dev/null
+++ b/examples/cs101_output/homework1.py
@@ -0,0 +1,17 @@
+# homework1.py
+def myfun(): 
+    """
+    Simple aux references eq. (1) in Section 1.
+    Simple bibtex citations: (Ber07) and (Her21, Somewhere around the middle)
+
+    Example of custom command (reference notes)
+    > \nref{fig1}
+
+    Other example of custom command (reference assignment)
+    > (Assignment 2, Section 1)
+    """
+    print("See \Section 1")  # Also works.
+    return 42 
+    s1 = sum(l1) 
+    s2 = sum(l2)
+    print("Hello worlds!") 
\ No newline at end of file
diff --git a/examples/cs101_output/i_tag_a.shell b/examples/cs101_output/i_tag_a.shell
new file mode 100644
index 0000000000000000000000000000000000000000..f753af9e071d5f0575212af7b8d4acbbe5abc9e3
--- /dev/null
+++ b/examples/cs101_output/i_tag_a.shell
@@ -0,0 +1,6 @@
+>>> for animal in ["Dog", "cat", "wolf"]:
+...     print("An example of a four legged animal is", animal)
+... 
+An example of a four legged animal is Dog
+An example of a four legged animal is cat
+An example of a four legged animal is wolf
\ No newline at end of file
diff --git a/examples/cs101_output/i_tag_b.shell b/examples/cs101_output/i_tag_b.shell
new file mode 100644
index 0000000000000000000000000000000000000000..da664554997eaf0d2ecb6fdb239a2a4d2a8e51aa
--- /dev/null
+++ b/examples/cs101_output/i_tag_b.shell
@@ -0,0 +1,5 @@
+>>> def myfun(a,b):
+...     return a+b
+...
+>>> myfun(3,4)
+7
\ No newline at end of file
diff --git a/examples/cs101_output/o_tag_a.txt b/examples/cs101_output/o_tag_a.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5ea6232c69db1b9dc1922843d40cdd2a76e7abe3
--- /dev/null
+++ b/examples/cs101_output/o_tag_a.txt
@@ -0,0 +1,5 @@
+Here are the first 4 square numbers
+1 is a square
+4 is a square
+9 is a square
+16 is a square
diff --git a/examples/cs101_output/o_tag_b.txt b/examples/cs101_output/o_tag_b.txt
new file mode 100644
index 0000000000000000000000000000000000000000..171adc7942551e0e215427f9ee29e464dbee78cd
--- /dev/null
+++ b/examples/cs101_output/o_tag_b.txt
@@ -0,0 +1,3 @@
+Area of square of width 2 and height 4 is:
+8
+and that is a fact!
diff --git a/examples/cs101_output/s_tag.py b/examples/cs101_output/s_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcddb99bef1e9f08f28e0b729a35df04b70ed66b
--- /dev/null
+++ b/examples/cs101_output/s_tag.py
@@ -0,0 +1,39 @@
+# s_tag.py
+print("Area of square of width", width, "and height", height, "is:") 
+print(width*height)  #!s  # This is an example of a simple cutout
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+
+def primes_sieve(limit): 
+print(width*height)  
+print("and that is a fact!")
+print("An extra cutout") #!s #!s  # This will be added to the above cutout
+
+def primes_sieve(limit): 
+    limitn = limit+1
+    primes = range(2, limitn)
+
+    for i in primes: 
+print("An extra cutout") 
+
+def primes_sieve(limit): 
+    limitn = limit+1
+    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)  
+print("An extra cutout") 
+
+def primes_sieve(limit): 
+    limitn = limit+1
+    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 
\ No newline at end of file
diff --git a/examples/cs101_students/b_tag.py b/examples/cs101_students/b_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0e5de3b46df099311b67cd5b0d7f515bfbbf46f
--- /dev/null
+++ b/examples/cs101_students/b_tag.py
@@ -0,0 +1,12 @@
+"""
+"""
+def primes_sieve(limit):
+    # TODO: 8 lines missing.
+    raise NotImplementedError("Compute the list `primes` here of all primes up to `limit`")
+    return primes
+
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:")
+# TODO: 1 lines missing.
+raise NotImplementedError("Compute and print area here")
+print("and that is a fact!")
diff --git a/examples/cs101_students/f_tag.py b/examples/cs101_students/f_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1e55a7fd78858690177d4eb2d43cf0353375eb4
--- /dev/null
+++ b/examples/cs101_students/f_tag.py
@@ -0,0 +1,7 @@
+"""
+"""
+def myfun(a,b): 
+    """ The doc-string is not removed. """
+    # TODO: 1 lines missing.
+    raise NotImplementedError("return the sum of a and b")
+    return sm
diff --git a/examples/cs101_students/i_tag.py b/examples/cs101_students/i_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..59106b7567d50d6935afdb9d53570015d2c40c20
--- /dev/null
+++ b/examples/cs101_students/i_tag.py
@@ -0,0 +1,10 @@
+"""
+"""
+for animal in ["Dog", "cat", "wolf"]: 
+    print("An example of a four legged animal is", animal) 
+
+#!i=b
+def myfun(a,b):
+    return a+b
+myfun(3,4) 
+# Snipper will automatically insert an 'enter' after the function definition.
diff --git a/examples/cs101_students/o_tag.py b/examples/cs101_students/o_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0d31b05a3e6e6603134446eef6125bf53812a58
--- /dev/null
+++ b/examples/cs101_students/o_tag.py
@@ -0,0 +1,11 @@
+"""
+"""
+if __name__ == "__main__":
+    print("Here are the first 4 square numbers") 
+    for k in range(1,5):
+        print(k*k, "is a square")
+    print("This line will not be part of a cutout.")
+    width, height = 2, 4 
+    print("Area of square of width", width, "and height", height, "is:")
+    print(width*height)
+    print("and that is a fact!") 
diff --git a/examples/cs101_students/references.py b/examples/cs101_students/references.py
new file mode 100644
index 0000000000000000000000000000000000000000..0fd9ab5868c5952682abd8443aec97fda5a88125
--- /dev/null
+++ b/examples/cs101_students/references.py
@@ -0,0 +1,18 @@
+"""
+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():
+    """
+    Simple aux references eq. (1) in Section 1.
+    Simple bibtex citations: (Ber07) and (Her21, Somewhere around the middle)
+
+    Example of custom command (reference notes)
+    > (Her21, Figure 1)
+
+    Other example of custom command (reference assignment)
+    > (Assignment 2, Section 1)
+    """
+    print("See Section 1")  # Also works.
+    return 42
diff --git a/examples/cs101_students/s_tag.py b/examples/cs101_students/s_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..102fcafec9ce3e86bc09357f09a9bacc4b73849f
--- /dev/null
+++ b/examples/cs101_students/s_tag.py
@@ -0,0 +1,18 @@
+"""
+"""
+width, height = 2, 4
+print("Area of square of width", width, "and height", height, "is:") 
+print(width*height)  
+print("and that is a fact!")
+print("An extra cutout") 
+
+def primes_sieve(limit): 
+    limitn = limit+1
+    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 
diff --git a/examples/process_cs101.py b/examples/process_cs101.py
index 0268b192b2690b67385f69724140a42e962becd3..00bebbcfd22645aa255fec2afccb78299eef15ff 100644
--- a/examples/process_cs101.py
+++ b/examples/process_cs101.py
@@ -1,25 +1,3 @@
-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
+    from snipper.snip_dir import snip_dir
+    snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output")
diff --git a/examples/process_cs101_references.py b/examples/process_cs101_references.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f90dbadab8e4d2e55352a254a22db71e1f3d9e1
--- /dev/null
+++ b/examples/process_cs101_references.py
@@ -0,0 +1,15 @@
+from snipper.snip_dir import snip_dir
+from snipper.load_citations import get_aux, get_bibtex
+
+def main():
+    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, %s)", aux=auxfile),
+                                dict(command='\\nref', output="\cite[%s]{herlau}", aux=auxfile),
+                               ])
+    snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output", references=references)
+
+if __name__ == "__main__":
+    main()
diff --git a/src/snipper/__pycache__/block_parsing.cpython-38.pyc b/src/snipper/__pycache__/block_parsing.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..111a915440a38d68e7f4fef83ab63cc6392d7a7e
Binary files /dev/null and b/src/snipper/__pycache__/block_parsing.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/fix_bf.cpython-38.pyc b/src/snipper/__pycache__/fix_bf.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..eb22389410ff1cb4db353975d3c62a897782d50c
Binary files /dev/null and b/src/snipper/__pycache__/fix_bf.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/fix_cite.cpython-38.pyc b/src/snipper/__pycache__/fix_cite.cpython-38.pyc
index f4f2a6a15da1ceb5b8ad897d8a875183ee0e919c..de2b1ae6ef85a05fa5550eb02dee7e157751f4bc 100644
Binary files a/src/snipper/__pycache__/fix_cite.cpython-38.pyc and b/src/snipper/__pycache__/fix_cite.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/fix_o.cpython-38.pyc b/src/snipper/__pycache__/fix_o.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3ea6cd8ef8b3a3776ae9e2d534579277d4ab91b8
Binary files /dev/null and b/src/snipper/__pycache__/fix_o.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/fix_s.cpython-38.pyc b/src/snipper/__pycache__/fix_s.cpython-38.pyc
index b161b2c89e36fa9e9f5a4b97990c7c3ba4833196..b0366cef21b20d77bc837f78f88a9f18d9d97a4b 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/__pycache__/legacy.cpython-38.pyc b/src/snipper/__pycache__/legacy.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ca245802a659eb097945ed2488813d2fc128f72b
Binary files /dev/null and b/src/snipper/__pycache__/legacy.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/load_citations.cpython-38.pyc b/src/snipper/__pycache__/load_citations.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e79c0857a12b2136f7aa6dcb235a3dc9c4293688
Binary files /dev/null and b/src/snipper/__pycache__/load_citations.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/snip_dir.cpython-38.pyc b/src/snipper/__pycache__/snip_dir.cpython-38.pyc
index 50eb77d4a6c15593ccc6bfb963a021aa51a4b489..a8205b9d82839835b55f286b4d28b2c5c8467e71 100644
Binary files a/src/snipper/__pycache__/snip_dir.cpython-38.pyc and b/src/snipper/__pycache__/snip_dir.cpython-38.pyc differ
diff --git a/src/snipper/__pycache__/snipper_main.cpython-38.pyc b/src/snipper/__pycache__/snipper_main.cpython-38.pyc
index 6a50df2c7fb13fcde076d5ddf2d54c8687e166e5..21d84830317e97a60e1ba13d898e5f34d4cfb140 100644
Binary files a/src/snipper/__pycache__/snipper_main.cpython-38.pyc and b/src/snipper/__pycache__/snipper_main.cpython-38.pyc differ
diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py
index 0b1009c05c3266267f825bf7bb5d760fd4fd83f6..a386989177ae66191a7f299d941cfba9ad33b157 100644
--- a/src/snipper/block_parsing.py
+++ b/src/snipper/block_parsing.py
@@ -1,3 +1,7 @@
+"""
+Module for new-style block parsing. Make sure all functions are up-to-date and used.
+"""
+
 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)
@@ -26,10 +30,15 @@ def block_split(lines, tag):
     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):]
+        tag_args = ((line[:k + 1] if k >= 0 else line)[len(tag):] ).strip()
+
         if len(tag_args) == 0:
             return {'': ''}  # No name.
+
+        # print("TAG ARGS")
+        # print(tag_args)
         tag_args = dict([t.split("=") for t in tag_args.split(";")])
         return tag_args
 
diff --git a/src/snipper/fix_bf.py b/src/snipper/fix_bf.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..36ac1ce30fa5620266714f5d0b95d7867ebbf979 100644
--- a/src/snipper/fix_bf.py
+++ b/src/snipper/fix_bf.py
@@ -0,0 +1,67 @@
+import functools
+
+from snipper.legacy import indent, gcoms, block_process
+
+
+def fix_f(lines, debug):
+    lines2 = []
+    i = 0
+    while i < len(lines):
+        l = lines[i]
+        dx = l.find("#!f")
+        if dx >= 0:
+            l_head = l[dx+3:].strip()
+            l = l[:dx]
+            lines2.append(l)
+            id = indent(lines[i+1])
+            for j in range(i+1, 10000):
+                jid = len( indent(lines[j]) )
+                if  j+1 == len(lines) or ( jid < len(id) and len(lines[j].strip() ) > 0):
+                    break
+
+            if len(lines[j-1].strip()) == 0:
+                j = j - 1
+            funbody = "\n".join( lines[i+1:j] )
+            if i == j:
+                raise Exception("Empty function body")
+            i = j
+            comments, funrem = gcoms(funbody)
+            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
+
+
+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}")']
+
+        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
diff --git a/src/snipper/fix_cite.py b/src/snipper/fix_cite.py
index 7fbc1b44a4728216c0c5bdad52d2ace5d239a0e5..ad5b5ee60c5fe2b24a9ece2dffb274e4629be185 100644
--- a/src/snipper/fix_cite.py
+++ b/src/snipper/fix_cite.py
@@ -1,14 +1,19 @@
 from snipper.load_citations import find_tex_cite
-from snipper.snipper_main import COMMENT
+from snipper.legacy import COMMENT
 
 
-def fix_citations():
-    # This should be the master function.
-    pass
+def fix_citations(lines, references, strict=True):
+    lines = fix_aux(lines, aux=references.get('aux', {}) )
+    for cm in references.get('commands', []):
+        lines = fix_aux_special(lines, aux=cm['aux'], command=cm['command'], output=cm['output'])
+
+    lines = fix_bibtex(lines, bibtex=references.get('bibtex', {}))
+    return lines
 
 
-def fix_aux_special(lines, aux, command='\\nref', bibref='herlau'):
-    daux = {name: {'nicelabel': f'\\cite[' + v["nicelabel"] + ']{' + bibref + "}"} for name, v in aux.items()}
+def fix_aux_special(lines, aux, command='\\nref', output='\cite[%s]{my_bibtex_entry}'):
+    # out = output %
+    daux = {name: {'nicelabel': output % v['nicelabel'] } for name, v in aux.items()}
     l2 = fix_single_reference(lines, aux=daux, cmd=command, strict=True)
     return l2
 
@@ -18,7 +23,6 @@ def fix_aux(lines, aux, strict=True):
     return l2
 
 def fix_bibtex(lines, bibtex):
-    # lines = fix_references(lines, info, strict=strict)
     s = "\n".join(lines)
     i = 0
     all_refs = []
@@ -37,7 +41,6 @@ def fix_bibtex(lines, bibtex):
         s = s[:i] + rtxt + s[j+1:]
         i = i + len(rtxt)
 
-    cpr = ""
     if not s.startswith(COMMENT):
         s = f"{COMMENT}\n{COMMENT}\n" + s
     if len(all_refs) > 0:
@@ -51,6 +54,7 @@ def fix_bibtex(lines, bibtex):
 
 
 def fix_references(lines, info, strict=True):
+    assert False
     for cmd in info['new_references']:
         lines = fix_single_reference(lines, cmd, info['new_references'][cmd], strict=strict)
     return lines
diff --git a/src/snipper/fix_i.py b/src/snipper/fix_i.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/snipper/fix_o.py b/src/snipper/fix_o.py
new file mode 100644
index 0000000000000000000000000000000000000000..5caf0ac28801069d9a1b1bfc162b53010ded2b87
--- /dev/null
+++ b/src/snipper/fix_o.py
@@ -0,0 +1,38 @@
+import functools
+import os
+
+from snipper.legacy import indent, block_process
+
+
+def run_o(lines, file, output):
+    def block_fun(lines, start_extra, end_extra, art, output, **kwargs):
+        id = indent(lines[0])
+        outf = output + ("_" + art if art is not None else "") + ".txt"
+        l2 = []
+        l2 += [id + "import sys", id + f"sys.stdout = open('{outf}', 'w')"]
+        l2 += lines
+        # l2 += [indent(lines[-1]) + "sys.stdout.close()"]
+        l2 += [indent(lines[-1]) + "sys.stdout = sys.__stdout__"]
+        return l2, None
+    try:
+        lines2, didfind, extra, _ = block_process(lines, tag="#!o", block_fun=functools.partial(block_fun, output=output) )
+    except Exception as e:
+        print("Bad file: ", file)
+        print("I was cutting the #!o tag")
+        print("\n".join( lines) )
+        raise(e)
+
+    if didfind:
+        fp, ex = os.path.splitext(file)
+        file_run = fp + "_RUN_OUTPUT_CAPTURE" +ex
+        if os.path.exists(file_run):
+            print("file found mumble...")
+        else:
+            with open(file_run, 'w', encoding="utf-8") as f:
+                f.write("\n".join(lines2) )
+            cmd = "python " + file_run
+            import subprocess
+            s = subprocess.check_output(cmd, shell=True)
+            # s,ok = execute_command(cmd.split(), shell=True)
+            print(s)
+            os.remove(file_run)
\ No newline at end of file
diff --git a/src/snipper/legacy.py b/src/snipper/legacy.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1220397cfe157d5a0eb4e77c5a68f5d8443d04c
--- /dev/null
+++ b/src/snipper/legacy.py
@@ -0,0 +1,75 @@
+def indent(l):
+    v = len(l) - len(l.lstrip())
+    return l[:v]
+
+
+COMMENT = '"""'
+
+
+def gcoms(s):
+    coms = []
+    while True:
+        i = s.find(COMMENT)
+        if i >= 0:
+            j = s.find(COMMENT, i+len(COMMENT))+3
+        else:
+            break
+        if j < 0:
+            raise Exception("comment tag not closed")
+        coms.append(s[i:j])
+        s = s[:i] + s[j:]
+        if len(coms) > 10:
+            print("long comments in file", i)
+    return coms, s
+
+
+def block_process(lines, tag, block_fun):
+    i = 0
+    didfind = False
+    lines2 = []
+    block_out = []
+    cutout = []
+    while i < len(lines):
+        l = lines[i]
+        dx = l.find(tag)
+        if dx >= 0:
+            if l.find(tag, dx+1) > 0:
+                j = i
+            else:
+                for j in range(i + 1, 10000):
+                    if j >= len(lines):
+                        print("\n".join(lines))
+                        print("very bad end-line j while fixing tag", tag)
+                        raise Exception("Bad line while fixing", tag)
+                    if lines[j].find(tag) >= 0:
+                        break
+
+            pbody = lines[i:j+1]
+            if i == j:
+                start_extra = lines[j][dx:lines[j].rfind(tag)].strip()
+                end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip()
+            else:
+                start_extra = lines[i][dx:].strip()
+                end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip()
+
+            cutout.append(pbody)
+            tmp_ = start_extra.split("=")
+            arg = None if len(tmp_) <= 1 else tmp_[1].split(" ")[0]
+            start_extra = ' '.join(start_extra.split(" ")[1:] )
+
+            pbody[0] = pbody[0][:dx]
+            if j > i:
+                pbody[-1] = pbody[-1][:pbody[-1].find(tag)]
+
+            nlines, extra = block_fun(lines=pbody, start_extra=start_extra, end_extra=end_extra, art=arg, head=lines[:i], tail=lines[j+1:])
+            lines2 += nlines
+            block_out.append(extra)
+            i = j+1
+            didfind = True
+            if "!b" in end_extra:
+                assert(False)
+        else:
+            lines2.append(l)
+            i += 1
+
+    return lines2, didfind, block_out, cutout
\ No newline at end of file
diff --git a/src/snipper/snip_dir.py b/src/snipper/snip_dir.py
index 15657afecaa9afa3748157dd02c6b70c4687de0d..6032800f8bcaede58874b7ffd14fe3595fa2c339 100644
--- a/src/snipper/snip_dir.py
+++ b/src/snipper/snip_dir.py
@@ -16,41 +16,46 @@ def snip_dir(source_dir,  # Sources
 
     if exclude == None:
         exclude = []
+
     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 os.path.samefile( source_dir, dest_dir):
         raise Exception("Source and destination is the same")
+
     if clean_destination_dir:
         shutil.rmtree(dest_dir)
     os.makedirs(dest_dir)
-    # Now the destination dir is set up.
+
 
     out = dest_dir
     hw = {'base': source_dir,
           'exclusion': exclude}
 
     print(f"[snipper] Synchronizing directories: {hw['base']} -> {out}")
-    if os.path.exists(out):
-        shutil.rmtree(out)
+    if os.path.exists(dest_dir):
+        shutil.rmtree(dest_dir)
 
     base = hw['base']
 
-    shutil.copytree(base, out)
+    shutil.copytree(base, dest_dir)
     time.sleep(0.2)
 
-    ls = list(Path(out).glob('**/*.*'))
+    ls = list(Path(dest_dir).glob('**/*.*'))
     acceptable = []
     for l in ls:
-        split = os.path.normpath(os.path.relpath(l, out))
+        split = os.path.normpath(os.path.relpath(l, dest_dir))
         m = [fnmatch.fnmatch(split, ex) for ex in exclude]
         acceptable.append( (l, not any(m) ))
 
     # print(acceptable)
     # now we have acceptable files in list.
-
-    run_out_dirs = ["./output"]
+    # run_out_dirs = ["./output"]
     n = 0
     # edirs = [os.path.join(out, f_) for f_ in hw['exclusion']]  # Exclude directories on exclude list (speed)
     # edirs = {os.path.normpath(os.path.dirname(f_) if not os.path.isdir(f_) else f_) for f_ in edirs}
@@ -74,7 +79,11 @@ def snip_dir(source_dir,  # Sources
             kwargs = {}
             cut_files = True
             run_files = True
-            nrem = censor_file(f, info, paths, run_files=run_files, run_out_dirs=run_out_dirs[:1], cut_files=cut_files, solution_list=solution_list, include_path_base=base, **kwargs)
+            nrem = censor_file(f, info, run_files=run_files, run_out_dirs=output_dir, cut_files=cut_files, solution_list=solution_list,
+                               include_path_base=base,
+                               base_path=dest_dir,
+                               references=references,
+                               **kwargs)
             if nrem > 0:
                 print(f"{nrem}> {f}")
             n += nrem
diff --git a/src/snipper/snipper_main.py b/src/snipper/snipper_main.py
index dc6bbdcbdd5ccda5e590f0eb49938ec3192b8898..2425f10e4ab3a80a00d0f227247d85020e9fe548 100644
--- a/src/snipper/snipper_main.py
+++ b/src/snipper/snipper_main.py
@@ -3,11 +3,10 @@ import functools
 import textwrap
 import re
 # from snipper.fix_s import save_s
+from snipper.fix_bf import fix_f, fix_b2
+from snipper.fix_o import run_o
+from snipper.legacy import indent, block_process
 
-COMMENT = '"""'
-def indent(l):
-    v = len(l) - len(l.lstrip())
-    return l[:v]
 
 def fix_r(lines):
     for i,l in enumerate(lines):
@@ -15,21 +14,6 @@ def fix_r(lines):
             lines[i] = indent(l) + l[l.find("#!r") + 3:].lstrip()
     return lines
 
-def gcoms(s):
-    coms = []
-    while True:
-        i = s.find(COMMENT)
-        if i >= 0:
-            j = s.find(COMMENT, i+len(COMMENT))+3
-        else:
-            break
-        if j < 0:
-            raise Exception("comment tag not closed")
-        coms.append(s[i:j])
-        s = s[:i] + s[j:]
-        if len(coms) > 10:
-            print("long comments in file", i)
-    return coms, s
 
 def strip_tag(lines, tag):
     lines2 = []
@@ -43,57 +27,6 @@ def strip_tag(lines, tag):
             lines2.append(l)
     return lines2
 
-def block_process(lines, tag, block_fun):
-    i = 0
-    didfind = False
-    lines2 = []
-    block_out = []
-    cutout = []
-    while i < len(lines):
-        l = lines[i]
-        dx = l.find(tag)
-        if dx >= 0:
-            if l.find(tag, dx+1) > 0:
-                j = i
-            else:
-                for j in range(i + 1, 10000):
-                    if j >= len(lines):
-                        print("\n".join(lines))
-                        print("very bad end-line j while fixing tag", tag)
-                        raise Exception("Bad line while fixing", tag)
-                    if lines[j].find(tag) >= 0:
-                        break
-
-            pbody = lines[i:j+1]
-            if i == j:
-                start_extra = lines[j][dx:lines[j].rfind(tag)].strip()
-                end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip()
-            else:
-                start_extra = lines[i][dx:].strip()
-                end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip()
-
-            cutout.append(pbody)
-            tmp_ = start_extra.split("=")
-            arg = None if len(tmp_) <= 1 else tmp_[1].split(" ")[0]
-            start_extra = ' '.join(start_extra.split(" ")[1:] )
-
-            pbody[0] = pbody[0][:dx]
-            if j > i:
-                pbody[-1] = pbody[-1][:pbody[-1].find(tag)]
-
-            nlines, extra = block_fun(lines=pbody, start_extra=start_extra, end_extra=end_extra, art=arg, head=lines[:i], tail=lines[j+1:])
-            lines2 += nlines
-            block_out.append(extra)
-            i = j+1
-            didfind = True
-            if "!b" in end_extra:
-                assert(False)
-        else:
-            lines2.append(l)
-            i += 1
-
-    return lines2, didfind, block_out, cutout
-
 
 def rem_nonprintable_ctrl_chars(txt):
     """Remove non_printable ascii control characters """
@@ -203,102 +136,6 @@ def run_i(lines, file, output):
     return lines
 
 
-def run_o(lines, file, output):
-    def block_fun(lines, start_extra, end_extra, art, output, **kwargs):
-        id = indent(lines[0])
-        outf = output + ("_" + art if art is not None else "") + ".txt"
-        l2 = []
-        l2 += [id + "import sys", id + f"sys.stdout = open('{outf}', 'w')"]
-        l2 += lines
-        # l2 += [indent(lines[-1]) + "sys.stdout.close()"]
-        l2 += [indent(lines[-1]) + "sys.stdout = sys.__stdout__"]
-        return l2, None
-    try:
-        lines2, didfind, extra, _ = block_process(lines, tag="#!o", block_fun=functools.partial(block_fun, output=output) )
-    except Exception as e:
-        print("Bad file: ", file)
-        print("I was cutting the #!o tag")
-        print("\n".join( lines) )
-        raise(e)
-
-    if didfind:
-        fp, ex = os.path.splitext(file)
-        file_run = fp + "_RUN_OUTPUT_CAPTURE" +ex
-        if os.path.exists(file_run):
-            print("file found mumble...")
-        else:
-            with open(file_run, 'w', encoding="utf-8") as f:
-                f.write("\n".join(lines2) )
-            cmd = "python " + file_run
-
-            s,ok = execute_command(cmd.split(), shell=True)
-            print(s)
-            os.remove(file_run)
-
-def fix_f(lines, debug):
-    lines2 = []
-    i = 0
-    while i < len(lines):
-        l = lines[i]
-        dx = l.find("#!f")
-        if dx >= 0:
-            l_head = l[dx+3:].strip()
-            l = l[:dx]
-            lines2.append(l)
-            id = indent(lines[i+1])
-            for j in range(i+1, 10000):
-                jid = len( indent(lines[j]) )
-                if  j+1 == len(lines) or ( jid < len(id) and len(lines[j].strip() ) > 0):
-                    break
-
-            if len(lines[j-1].strip()) == 0:
-                j = j - 1
-            funbody = "\n".join( lines[i+1:j] )
-            if i == j:
-                raise Exception("Empty function body")
-            i = j
-            comments, funrem = gcoms(funbody)
-            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
-
-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}")']
-
-        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
-
-
-
 def full_strip(lines, tags=None):
     if tags is None:
         tags = ["#!s", "#!o", "#!f", "#!b"]
@@ -315,10 +152,18 @@ 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,
+def censor_file(file, info, run_files=True, run_out_dirs=None, cut_files=True, solution_list=None,
                 censor_files=True,
+                base_path=None,
                 include_path_base=None,
-                strict=True):
+                strict=True,
+                references=None):
+
+    if references == None:
+        references = {}
+
+    from snipper.fix_cite import fix_citations
+
     dbug = False
     with open(file, 'r', encoding='utf8') as f:
         s = f.read()
@@ -330,8 +175,8 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files=
             lines[k] = l.replace("# !", "#!")
 
         try:
-            s = fix_cite(lines, info, strict=strict)
-            lines = s.split("\n")
+            lines = fix_citations(lines, references, strict=strict)
+            # lines = s.split("\n")
         except IndexError as e:
             print(e)
             print("Fuckup in file, cite/reference tag not found!>", file)
@@ -339,7 +184,7 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files=
 
         if run_files or cut_files:
             ofiles = []
-            for rod in run_out_dirs:
+            for rod in [run_out_dirs]:
                 # if not os.path.isdir(rod):
                 #     os.mkdir(rod)
                 ofiles.append(os.path.join(rod, os.path.basename(file).split(".")[0]) )
@@ -349,7 +194,9 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files=
                 run_o(lines, file=file, output=ofiles[0])
                 run_i(lines, file=file, output=ofiles[0])
             if cut_files:
-                save_s(lines, file=file, output=ofiles[0], include_path_base=include_path_base)  # save file snips to disk
+                from snipper.fix_s import save_s
+
+                save_s(lines, file_path=os.path.relpath(file, base_path), output_dir=run_out_dirs)  # save file snips to disk
         lines = full_strip(lines, ["#!s", "#!o", '#!i'])
 
         # lines = fix_c(lines)