├── .gitignore ├── testdata ├── if-archive │ ├── tagtest │ │ ├── file │ │ ├── dir1 │ │ │ ├── file │ │ │ ├── sub1 │ │ │ │ ├── file │ │ │ │ └── Index │ │ │ ├── sub2 │ │ │ │ ├── file │ │ │ │ └── Index │ │ │ └── Index │ │ ├── dir2 │ │ │ ├── file │ │ │ └── Index │ │ ├── dir3 │ │ │ ├── file │ │ │ ├── sub1 │ │ │ │ ├── file │ │ │ │ └── Index │ │ │ └── Index │ │ ├── file-porn │ │ ├── file-safe │ │ └── Index │ ├── space dir │ │ ├── q&&&r │ │ ├── hello(...)?! │ │ ├── ~&
│ │ ├── "ascquot" │ │ │ ├── dir │ │ │ │ └── "file" │ │ │ └── Index │ │ ├── space file.txt │ │ └── Index │ ├── compfiles │ ├── games │ │ ├── game1 │ │ ├── game2 │ │ ├── all.zip │ │ ├── old │ │ │ ├── game1-v1 │ │ │ ├── older │ │ │ │ ├── game1-pre │ │ │ │ └── Index │ │ │ └── Index │ │ ├── bothgames │ │ └── Index │ ├── art │ │ ├── foo.z5 │ │ ├── x&all.zip │ │ └── Index │ ├── comp │ │ ├── files │ │ │ ├── file-1.txt │ │ │ ├── file-2.txt │ │ │ ├── file-3.txt │ │ │ ├── Index │ │ │ └── all.zip │ │ ├── leftfoot.blorb │ │ └── rightfoot.blorb │ ├── compfile1 │ ├── Index │ └── Master-Index ├── metadata │ └── if-archive │ │ ├── games │ │ ├── old.txt │ │ ├── game2.txt │ │ ├── game2.json │ │ ├── old.json │ │ ├── bothgames.json │ │ ├── bothgames.txt │ │ ├── game1.txt │ │ ├── game2.xml │ │ ├── old.xml │ │ ├── old │ │ │ └── older │ │ │ │ ├── game1-pre.txt │ │ │ │ ├── game1-pre.json │ │ │ │ └── game1-pre.xml │ │ ├── game1.json │ │ ├── bothgames.xml │ │ └── game1.xml │ │ ├── art │ │ ├── x&all.zip.json │ │ ├── x&all.zip.txt │ │ └── x&all.zip.xml │ │ ├── tagtest │ │ ├── file-safe.json │ │ ├── file-safe.txt │ │ ├── file-porn.json │ │ ├── file-porn.txt │ │ ├── file-safe.xml │ │ └── file-porn.xml │ │ └── comp │ │ └── files │ │ ├── file-1.txt.json │ │ ├── file-1.txt.txt │ │ ├── all.zip.json │ │ ├── all.zip.txt │ │ ├── file-1.txt.xml │ │ └── all.zip.xml ├── index.html ├── set-timestamps.py ├── blocktag.map ├── indexes │ ├── if-archive │ │ ├── tagtest │ │ │ ├── dir2 │ │ │ │ └── index.html │ │ │ ├── dir1 │ │ │ │ ├── sub2 │ │ │ │ │ └── index.html │ │ │ │ ├── sub1 │ │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ │ ├── dir3 │ │ │ │ ├── sub1 │ │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── space dir │ │ │ ├── "ascquot" │ │ │ │ ├── dir │ │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── comp │ │ │ ├── index.html │ │ │ └── files │ │ │ │ └── index.html │ │ ├── games │ │ │ ├── old │ │ │ │ ├── older │ │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── art │ │ │ └── index.html │ │ └── index.html │ ├── datev_1.html │ ├── date_1.html │ ├── dirlist.html │ ├── dirmap.html │ ├── date_2.html │ ├── archive.rss │ ├── date_4.html │ ├── date_3.html │ ├── datev_2.html │ ├── date.html │ ├── datev_4.html │ └── datev_3.html └── checksum-cache.txt ├── requirements.txt ├── lib ├── map-skip-patterns ├── no-unbox-link ├── macros.html ├── footer.html ├── dirsafety.html ├── colindex.html ├── dirlist.html ├── tagdef.html ├── subdirentry.html ├── no-index-entry ├── blocktag-head.txt ├── rss.xml ├── dirmap.html ├── fileentry.html ├── metadata.html ├── page.html ├── datelist.html ├── xmlbase.xml └── main.html ├── upload-public-dl ├── end-to-end-test ├── nightly-backup ├── configparse.py ├── tests.py ├── make-master-index.py ├── aws-put.py ├── make-master-index ├── README.md └── uncache.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/q&&&r: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir2/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir3/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/file-porn: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/file-safe: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/compfiles: -------------------------------------------------------------------------------- 1 | comp/files -------------------------------------------------------------------------------- /testdata/if-archive/games/game1: -------------------------------------------------------------------------------- 1 | Game 1. 2 | -------------------------------------------------------------------------------- /testdata/if-archive/games/game2: -------------------------------------------------------------------------------- 1 | Game 2. 2 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/hello(...)?!: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/~&
: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/sub1/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/sub2/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir3/sub1/file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/if-archive/art/foo.z5: -------------------------------------------------------------------------------- 1 | Contents of foo.z5 2 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/files/file-1.txt: -------------------------------------------------------------------------------- 1 | File 1 2 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/files/file-2.txt: -------------------------------------------------------------------------------- 1 | File 2 2 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/files/file-3.txt: -------------------------------------------------------------------------------- 1 | File 3 2 | -------------------------------------------------------------------------------- /testdata/if-archive/compfile1: -------------------------------------------------------------------------------- 1 | comp/files/file-1.txt -------------------------------------------------------------------------------- /testdata/if-archive/space dir/"ascquot"/dir/"file": -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Markdown ~= 3.5 2 | Jinja2 ~= 3.1 3 | -------------------------------------------------------------------------------- /testdata/if-archive/games/all.zip: -------------------------------------------------------------------------------- 1 | ../comp/files/all.zip -------------------------------------------------------------------------------- /testdata/if-archive/games/old/game1-v1: -------------------------------------------------------------------------------- 1 | Game 1, old release. 2 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/sub1/Index: -------------------------------------------------------------------------------- 1 | 2 | # file 3 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir3/sub1/Index: -------------------------------------------------------------------------------- 1 | 2 | # file 3 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/leftfoot.blorb: -------------------------------------------------------------------------------- 1 | Contents of My Left Foot 2 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/space file.txt: -------------------------------------------------------------------------------- 1 | Space file contents 2 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/rightfoot.blorb: -------------------------------------------------------------------------------- 1 | Contents of Your Right Foot 2 | -------------------------------------------------------------------------------- /testdata/if-archive/games/old/older/game1-pre: -------------------------------------------------------------------------------- 1 | Game 1, pre-release. 2 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir2/Index: -------------------------------------------------------------------------------- 1 | blocktree: no 2 | 3 | # file 4 | -------------------------------------------------------------------------------- /testdata/if-archive/games/bothgames: -------------------------------------------------------------------------------- 1 | A package containing game1 and game2. 2 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/Index: -------------------------------------------------------------------------------- 1 | blockdir: text-porn 2 | 3 | # file 4 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir1/sub2/Index: -------------------------------------------------------------------------------- 1 | blocktree: no 2 | 3 | # file 4 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old.txt: -------------------------------------------------------------------------------- 1 | # if-archive/games/old 2 | tuid: abc12345 3 | -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/dir3/Index: -------------------------------------------------------------------------------- 1 | blocktree: yes 2 | blockdir: no 3 | 4 | # file 5 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game2.txt: -------------------------------------------------------------------------------- 1 | # if-archive/games/game2 2 | tuid: xyz222 3 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game2.json: -------------------------------------------------------------------------------- 1 | { 2 | "tuid": [ 3 | "xyz222" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old.json: -------------------------------------------------------------------------------- 1 | { 2 | "tuid": [ 3 | "abc12345" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/"ascquot"/Index: -------------------------------------------------------------------------------- 1 | 2 | # dir/"file" 3 | 4 | Deep description. 5 | 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/art/x&all.zip.json: -------------------------------------------------------------------------------- 1 | { 2 | "unbox-link": [ 3 | "false" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/art/x&all.zip.txt: -------------------------------------------------------------------------------- 1 | # if-archive/art/x&all.zip 2 | unbox-link: false 3 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-safe.json: -------------------------------------------------------------------------------- 1 | { 2 | "safety": [ 3 | "none" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-safe.txt: -------------------------------------------------------------------------------- 1 | # if-archive/tagtest/file-safe 2 | safety: none 3 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/file-1.txt.json: -------------------------------------------------------------------------------- 1 | { 2 | "ifwiki": [ 3 | "File One" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-porn.json: -------------------------------------------------------------------------------- 1 | { 2 | "safety": [ 3 | "visual-porn" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /lib/map-skip-patterns: -------------------------------------------------------------------------------- 1 | ^if-archive/games/competition\d+/ 2 | ^if-archive/games/springthing/\d+/ 3 | ^.*/old$ 4 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-porn.txt: -------------------------------------------------------------------------------- 1 | # if-archive/tagtest/file-porn 2 | safety: visual-porn 3 | -------------------------------------------------------------------------------- /testdata/index.html: -------------------------------------------------------------------------------- 1 | 2 | This way to the Archive. 3 | 4 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/file-1.txt.txt: -------------------------------------------------------------------------------- 1 | # if-archive/comp/files/file-1.txt 2 | ifwiki: File One 3 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/bothgames.json: -------------------------------------------------------------------------------- 1 | { 2 | "tuid": [ 3 | "xyz111", 4 | "xyz222" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/bothgames.txt: -------------------------------------------------------------------------------- 1 | # if-archive/games/bothgames 2 | tuid: xyz111 3 | tuid: xyz222 4 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/all.zip.json: -------------------------------------------------------------------------------- 1 | { 2 | "safety": [ 3 | "visual-porn, visual-gore" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/all.zip.txt: -------------------------------------------------------------------------------- 1 | # if-archive/comp/files/all.zip 2 | safety: visual-porn, visual-gore 3 | -------------------------------------------------------------------------------- /testdata/if-archive/art/x&all.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifarchive-ifmap-py/main/testdata/if-archive/art/x&all.zip -------------------------------------------------------------------------------- /testdata/if-archive/comp/files/Index: -------------------------------------------------------------------------------- 1 | # all.zip 2 | safety: visual-porn, visual-gore 3 | 4 | # file-1.txt 5 | ifwiki: File One 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game1.txt: -------------------------------------------------------------------------------- 1 | # if-archive/games/game1 2 | tuid: xyz111 3 | ifwiki: Adventure 4 | safety: text-porn 5 | -------------------------------------------------------------------------------- /testdata/if-archive/games/old/Index: -------------------------------------------------------------------------------- 1 | 2 | # older/game1-pre 3 | tuid: ghij987654 4 | 5 | Double-depth file reference (from lower down). 6 | -------------------------------------------------------------------------------- /testdata/if-archive/comp/files/all.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifarchive-ifmap-py/main/testdata/if-archive/comp/files/all.zip -------------------------------------------------------------------------------- /testdata/if-archive/tagtest/Index: -------------------------------------------------------------------------------- 1 | blocktree: yes 2 | 3 | 4 | # file 5 | 6 | # file-safe 7 | safety: none 8 | 9 | # file-porn 10 | safety: visual-porn 11 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tuid 4 | xyz222 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tuid 4 | abc12345 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/if-archive/games/old/older/Index: -------------------------------------------------------------------------------- 1 | ifwiki: Folder 2 | 3 | Directory description (in the directory). 4 | 5 | # game1-pre 6 | ifwiki: Game 7 | 8 | Game description. 9 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/art/x&all.zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | unbox-link 4 | false 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-safe.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | safety 4 | none 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/file-1.txt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ifwiki 4 | File One 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old/older/game1-pre.txt: -------------------------------------------------------------------------------- 1 | # if-archive/games/old/older/game1-pre 2 | ifwiki: Game 3 | tuid: ghi98765 4 | tuid: ghij987654 5 | safety: self-harm 6 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/tagtest/file-porn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | safety 4 | visual-porn 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game1.json: -------------------------------------------------------------------------------- 1 | { 2 | "tuid": [ 3 | "xyz111" 4 | ], 5 | "ifwiki": [ 6 | "Adventure" 7 | ], 8 | "safety": [ 9 | "text-porn" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/comp/files/all.zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | safety 4 | visual-porn, visual-gore 5 | 6 | 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/bothgames.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tuid 4 | xyz111 5 | xyz222 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/no-unbox-link: -------------------------------------------------------------------------------- 1 | if-archive/info/archive-stats 2 | if-archive/info/ifdb 3 | if-archive/info/ifwiki 4 | if-archive/info/intficforum 5 | if-archive/rec.arts.int-fiction 6 | if-archive/rec.games.int-fiction 7 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old/older/game1-pre.json: -------------------------------------------------------------------------------- 1 | { 2 | "ifwiki": [ 3 | "Game" 4 | ], 5 | "tuid": [ 6 | "ghi98765", 7 | "ghij987654" 8 | ], 9 | "safety": [ 10 | "self-harm" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/game1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tuid 4 | xyz111 5 | 6 | ifwiki 7 | Adventure 8 | 9 | safety 10 | text-porn 11 | 12 | 13 | -------------------------------------------------------------------------------- /testdata/metadata/if-archive/games/old/older/game1-pre.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ifwiki 4 | Game 5 | 6 | tuid 7 | ghi98765 8 | ghij987654 9 | 10 | safety 11 | self-harm 12 | 13 | 14 | -------------------------------------------------------------------------------- /upload-public-dl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Compress the entire (public) contents of the Archive and stream it to 4 | # a (public) AWS bucket. 5 | # 6 | # This requires secret credentials, so it must be run as root. 7 | 8 | cd /var/ifarchive/htdocs 9 | tar -czf - index.html misc indexes metadata if-archive | python3 /var/ifarchive/bin/aws-put.py --config /var/ifarchive/lib/aws.config --stdin ifarchive-all.tar.gz iftf-ifarchive-download 10 | -------------------------------------------------------------------------------- /lib/macros.html: -------------------------------------------------------------------------------- 1 | 2 | {%- macro wbrslash(ls) -%} 3 | {%- set slash = joiner('/') -%} 4 | {%- for el in ls.split('/') -%} 5 | {{ slash()|safe }}{{ el }} 6 | {%- endfor -%} 7 | {%- endmacro -%} 8 | 9 | {%- macro wbrlinkslash(ls) -%} 10 | {%- set slash = joiner('/') -%} 11 | {%- for totel, el in ls -%} 12 | {{ slash()|safe }}{{ el }} 13 | {%- endfor -%} 14 | {%- endmacro -%} 15 | -------------------------------------------------------------------------------- /testdata/if-archive/art/Index: -------------------------------------------------------------------------------- 1 | ifwiki: IF Art Show 2 | 3 | Files from an art show: 4 | 5 | 6 | # concept.txt 7 | 8 | The concept statement of the art show: 9 | 10 | A description paragraph. 11 | 12 | # contents.txt 13 | 14 | A table of contents 15 | 16 | - Entry 1 17 | - Entry 2 18 | - Entry 3 19 | 20 | # foo.z5 21 | 22 | *Foo*: An Art Project 23 | Release 1 / Serial number 000000 24 | 25 | # x&all.zip 26 | unbox-link: false 27 | 28 | -------------------------------------------------------------------------------- /lib/footer.html: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /end-to-end-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf testdata/indexes testdata/metadata 4 | 5 | python3 make-master-index.py testdata > testdata/if-archive/Master-Index 6 | 7 | # The ifmap.py script looks at file timestamps to decide the "last updated" 8 | # date of each file. Git doesn't preserve file timestamps, so we need 9 | # to manually set them to known values. 10 | python3 testdata/set-timestamps.py 11 | 12 | # Generate the index files. 13 | python3 ifmap.py --src lib --index testdata/if-archive/Master-Index --tree testdata --blockmap testdata/blocktag.map --curdate 2018-11-15T12:00:00+00:00 14 | 15 | -------------------------------------------------------------------------------- /testdata/if-archive/space dir/Index: -------------------------------------------------------------------------------- 1 | 2 | This directory tests funny filenames. 3 | 4 | Here are some index links: 5 | 6 | - 7 | - 8 | - 9 | - 10 | - 11 | - 12 | 13 | # aaa1 14 | 15 | These first few are just for spacing. 16 | 17 | # aaa2 18 | 19 | Test... 20 | 21 | # aaa3 22 | 23 | Test... 24 | 25 | # aaa4 26 | 27 | Test... 28 | 29 | # αβγδε 30 | 31 | # “author’s quotes” 32 | 33 | # 😀 34 | -------------------------------------------------------------------------------- /lib/dirsafety.html: -------------------------------------------------------------------------------- 1 | {% from 'tagdef.html' import tag_table %} 2 | 3 | {% if _dirsafety == ['unknown'] %} 4 |

5 | This directory has not been reviewed for content warnings. 6 | Unless specifically noted, all files may have content of any type. 7 |

8 | {% else %} 9 |

10 | This directory has the following content warning: 11 | {%- set comma = joiner(', ') %} 12 | {% for val in _dirsafety -%} 13 | {%- set taglabel, tagdesc = tag_table.get(val, (val, 'Explanation not available')) -%} 14 | {{ comma() }}{{ taglabel }} 15 | {%- endfor %}. 16 |

17 | {% endif %} 18 | -------------------------------------------------------------------------------- /lib/colindex.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /lib/dirlist.html: -------------------------------------------------------------------------------- 1 | {% extends "page.html" %} 2 | {% from 'macros.html' import wbrslash %} 3 | 4 | {% block description %} 5 |

6 | About the IF-Archive. 7 |

8 | 9 | {% include "colindex.html" %} 10 | 11 |

12 | Or, 13 | enter the Archive. 14 |

15 | {% endblock %} 16 | 17 | {% block body %} 18 | 19 |
20 | {% set parity = cycler("Even", "Odd") %} 21 | {% for dir in _dirs %} 22 |
{{ wbrslash(dir.dir) }} 23 | {% endfor %} 24 |
25 | 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /lib/tagdef.html: -------------------------------------------------------------------------------- 1 | {% set tag_table = { 2 | 'none': ('no warnings', 'No content warnings'), 3 | 'text-porn': ('pornographic text', 'Sexual content meant principly to arouse'), 4 | 'visual-porn': ('pornographic images', 'Sexual content meant principly to arouse'), 5 | 'visual-gore': ('violent or gory images', 'Violent images or depictions of gore'), 6 | 'self-harm': ('suicide or self-injury', 'Content which encourages or provides instructions for suicide or self-injury'), 7 | 'eating-disorder': ('disordered eating behavior', 'Content which encourages or provides instructions for an eating disorder or behavior associated with same'), 8 | 'unknown': ('not reviewed', 'Content has not been reviewed; any warnings may apply'), 9 | } %} 10 | -------------------------------------------------------------------------------- /lib/subdirentry.html: -------------------------------------------------------------------------------- 1 |
{{ subdir.name }} 2 | 3 | 4 | 5 | {%- if subdir.islink %} 6 | [link to {{ subdir.linkdir }}] 7 | {%- endif %} 8 | 9 | {%- if subdir._metadata %} 10 | {% set dat = subdir._metadata %} {% set datel = 'dd' %} 11 | {% include "metadata.html" %} 12 | {%- endif %} 13 | 14 | {%- if subdir.hasdesc %} 15 |
{% autoescape false %}{{ subdir.desc }}{% endautoescape %} 16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /lib/no-index-entry: -------------------------------------------------------------------------------- 1 | if-archive/README 2 | if-archive/directory-tree 3 | if-archive/new-since-last-post 4 | if-archive/Master-Index 5 | if-archive/md5.txt 6 | if-archive/ls-lR 7 | if-archive/games/appleII/eamon 8 | if-archive/games/competition 9 | if-archive/info/archive-stats 10 | if-archive/info/ifdb 11 | if-archive/info/ifwiki 12 | if-archive/info/intficforum 13 | if-archive/infocom/patches 14 | if-archive/infocom/compilers/zilf/old 15 | if-archive/programming/glk/old 16 | if-archive/programming/glk/implementations/old 17 | if-archive/programming/glulx/interpreters/glulxe/old 18 | if-archive/programming/glulx/interpreters/quixe/old 19 | if-archive/programming/glulx/interpreters/git/old 20 | if-archive/rec.arts.int-fiction 21 | if-archive/rec.games.int-fiction 22 | if-archive/unprocessed 23 | -------------------------------------------------------------------------------- /testdata/if-archive/games/Index: -------------------------------------------------------------------------------- 1 | 2 | Hey: these are some game files. 3 | 4 | - A directory link: 5 | - And a file link: 6 | - And a file index link: or 7 | 8 | # game1 9 | tuid: xyz111 10 | ifwiki: Adventure 11 | safety: text-porn 12 | 13 | Game number 1. 14 | 15 | # game2 16 | tuid: xyz222 17 | 18 | # bothgames 19 | tuid: xyz111 20 | xyz222 21 | 22 | # all.zip 23 | 24 | Linked to comp/files. 25 | 26 | # old 27 | tuid: abc12345 28 | 29 | The directory of old stuff. 30 | 31 | # old/older 32 | tuid: def56789 33 | 34 | A double-depth directory. 35 | 36 | # old/older/game1-pre 37 | tuid: ghi98765 38 | safety: self-harm 39 | 40 | Triple-depth file reference. 41 | -------------------------------------------------------------------------------- /lib/blocktag-head.txt: -------------------------------------------------------------------------------- 1 | # File blocking tags for the archiveblock plugin 2 | # See: https://github.com/iftechfoundation/ifarchive-archiveblock 3 | 4 | # This file is generated from the "safety" tags in the Index files. 5 | # Last generated: {{ date|isodate }} 6 | 7 | # Each line represents a file or directory which is blocked in the UK. 8 | # A pathname that ends with "/*" tags all the files in that directory. 9 | # A pathname that ends with "/**" tags all the files in that directory *and* 10 | # its subdirectories. 11 | 12 | # The next field is separated from the pathname by a tab. The field has 13 | # the form "FLAGS:TAGS". The FLAGS is empty or "u" (meaning "block this 14 | # file in the UK"). The TAGS are a comma-separated list of values; 15 | # see https://ifarchive.org/misc/org-procedures.html. 16 | 17 | -------------------------------------------------------------------------------- /lib/rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IFArchive Recent Additions 6 | https://ifarchive.org 7 | New additions to the Interactive Fiction Archive 8 | en-us 9 | tech@ifarchive.org (Archive Support) 10 | {{ changedate |isodate }} 11 | {{ curdate |isodate }} 12 | 13 | {% for file in _files %} 14 | 15 | {{ file.name }} 16 | https://ifarchive.org/{{ file.path |urlencode }} 17 | {{ file.path |urlencode }}::{{ file.date }} 18 | {{ file.date |isodate }} 19 | 20 | {%- if file.hasxmldesc %} 21 | {{ file.xmldesc }} 22 | {%- endif %} 23 | [at: {{ file.path |urlencode }}] 24 | 25 | 26 | {% endfor %} 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /nightly-backup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Sync the files tree (not indexes or other material) to an AWS bucket. 4 | # The bucket is configured with versioning and a 7-day expiration for 5 | # old file versions, so we get that much rolling history. 6 | 7 | # As of late 2024, this takes about 90 seconds to run for a no-change 8 | # sync. 9 | 10 | # This requires secret credentials, so it must be run as root. 11 | 12 | # Requires rclone 1.53 or later. 13 | # /var/ifarchive/lib/rclone.conf: rclone configuration (names AWS as the provider) 14 | # /var/ifarchive/lib/aws.config: AWS access keys (must be root to read) 15 | 16 | /usr/local/bin/rclone --verbose --config /var/ifarchive/lib/rclone.conf --s3-shared-credentials-file /var/ifarchive/lib/aws.config sync /var/ifarchive/htdocs/if-archive s3:iftf-ifarchive-backup --checksum --links --checkers 50 --transfers 10 &>> /var/ifarchive/logs/nightly-backup.log 17 | 18 | # Docs suggest a fast "top-up" of recent files, and then a full sync less often. I haven't set that up yet. See: https://rclone.org/s3/ 19 | -------------------------------------------------------------------------------- /testdata/if-archive/Index: -------------------------------------------------------------------------------- 1 | 2 | > _You feel somewhat disoriented as you pass through..._ 3 | 4 | This is the Interactive Fiction Archive at 5 | [ifarchive.org](/). 6 | 7 | You may be interested in... 8 | 9 | - The [README file for the Archive](/if-archive/README). 10 | - The [list of directories](/indexes/dirmap.html) in the Archive. 11 | - New arrivals in the [past week](/indexes/datev_1.html), [past month](/indexes/datev_2.html), [past three months](/indexes/datev_3.html), or [past year](/indexes/datev_4.html). 12 | - An [RSS feed](/indexes/archive.rss) of recent arrivals. 13 | - A list of [every file in the archive](/indexes/date.html) (tens of thousands!) 14 | - [Master-Index.xml](/indexes/Master-Index.xml), which describes every file in the Archive (very large XML file!) 15 | 16 | Or, go delving into the hierarchical tree shown here. 17 | 18 | # art 19 | 20 | Art stuff. 21 | 22 | # comp 23 | 24 | Competitions. This is a long line of text so we can see how the column width is going to work out in real life. 25 | 26 | # games 27 | 28 | Games stuff. 29 | -------------------------------------------------------------------------------- /lib/dirmap.html: -------------------------------------------------------------------------------- 1 | {% extends "page.html" %} 2 | {% from 'macros.html' import wbrslash %} 3 | 4 | {% block description %} 5 |

6 | This is a somewhat abbreviated list of the directories on the Archive. 7 | Directories for individual competition games and outdated game versions 8 | have been omitted. 9 | For a really absolutely complete list of directories, 10 | see here. 11 |

12 | 13 |

14 | About the IF-Archive. 15 |

16 | 17 | {% include "colindex.html" %} 18 | 19 |

20 | Or, 21 | enter the Archive. 22 |

23 | {% endblock %} 24 | 25 | {% block body %} 26 | 27 |
28 | {% set parity = cycler("Even", "Odd") %} 29 | {% for dir in _dirs %} 30 |
{{ wbrslash(dir.dir) }} 31 | {%- if dir.hasparentdesc %} 32 |
{% autoescape false %}{{ dir.parentdesc }}{% endautoescape %} 33 | {%- endif %} 34 | {% endfor %} 35 |
36 | 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /testdata/set-timestamps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This sets the timestamps of all the files under testdata/if-archive. 4 | # This lets us run ifmap.py on the test data with predictable results. 5 | 6 | import sys, os 7 | import time 8 | 9 | dirname = 'testdata/if-archive' 10 | if len(sys.argv) >= 2: 11 | dirname = sys.argv[1] 12 | 13 | common_timestamp = 1540000000 # 20 Oct 2018 14 | 15 | timestamps = { 16 | 'file': 1500000000, 17 | 'file-safe': 1500000000, 18 | 'file-porn': 1500000000, 19 | 'file-1.txt': 1539500000, 20 | 'file-2.txt': 1539600000, 21 | 'file-3.txt': 1539700000, 22 | 'all.zip': 1539700000, 23 | 'game1-v1': 1539000000, 24 | 'game1-pre': 1538800000, 25 | } 26 | 27 | for root, dirs, files in os.walk(dirname): 28 | for file in files: 29 | val = timestamps.get(file, common_timestamp) 30 | path = os.path.join(root, file) 31 | if os.path.islink(path): 32 | continue 33 | os.utime(path, times=(val, val)) 34 | timestr = time.strftime('%H:%M:%S %d-%b-%Y', time.gmtime(val)) 35 | #print('%s: %s' % (path, timestr)) 36 | 37 | -------------------------------------------------------------------------------- /configparse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Silly little script to grovel through ifarch.config for a key. 5 | This winds up being useful in the build-indexes script. 6 | 7 | The config file is at $IFARCHIVE_CONFIG, or /var/ifarchive/lib/ifarch.config 8 | if the env var is not set. 9 | """ 10 | 11 | import sys 12 | import os, os.path 13 | import configparser 14 | 15 | if len(sys.argv) <= 1: 16 | print('usage: configparse DOMAIN:KEY [ DEFAULT ]') 17 | sys.exit(1) 18 | 19 | keystr = sys.argv[1] 20 | defval = None 21 | if len(sys.argv) >= 3: 22 | defval = sys.argv[2] 23 | 24 | if ':' in keystr: 25 | domain, _, key = keystr.partition(':') 26 | else: 27 | key = keystr 28 | domain = 'DEFAULT' 29 | 30 | 31 | configpath = '/var/ifarchive/lib/ifarch.config' 32 | configpath = os.environ.get('IFARCHIVE_CONFIG', configpath) 33 | if not os.path.isfile(configpath): 34 | print('Config file not found: ' + configpath) 35 | sys.exit(1) 36 | 37 | 38 | config = configparser.ConfigParser() 39 | config.read(configpath) 40 | 41 | 42 | try: 43 | map = config[domain] 44 | result = map[key] 45 | except: 46 | if defval is None: 47 | print('Key not found: ' + keystr) 48 | sys.exit(1) 49 | result = defval 50 | 51 | print(result) 52 | sys.exit(0) 53 | -------------------------------------------------------------------------------- /lib/fileentry.html: -------------------------------------------------------------------------------- 1 |
{{ file.name }} 2 | 3 | 4 | 5 | {%- if file.date %} 6 | [{{ file.datestr }}] 7 | {%- endif %} 8 | 9 | {%- if file.islink %} 10 | [link to {{ file.nlinkpath }}] 11 | {%- endif %} 12 | 13 | {%- if file.hasunboxlink %} 14 |
View contents 15 | {%- endif %} 16 | 17 | {%- if file._metadata %} 18 | {% set dat = file._metadata %} {% set datel = 'dd' %} 19 | {% include "metadata.html" %} 20 | {%- endif %} 21 | 22 | {%- if file.hasdesc %} 23 |
{% autoescape false %}{{ file.desc }}{% endautoescape %} 24 | {%- endif %} 25 | 26 | {%- if file._backsymlinks %} 27 | {% for dfile in file._backsymlinks %} 28 |
[linked from {{ dfile.path }}] 29 | {% endfor %} 30 | {%- endif %} 31 | -------------------------------------------------------------------------------- /lib/metadata.html: -------------------------------------------------------------------------------- 1 | {% from 'tagdef.html' import tag_table %} 2 | 3 | {%- if dat.tuid %} 4 | {%- set comma = joiner(', ') %} 5 | <{{ datel }} class="FileData"> 6 | {%- for val in dat.tuid -%} 7 | {{ comma() }}{% if loop.first %}IFDB {% endif %}entry 8 | {%- endfor %} 9 | {%- endif %} 10 | 11 | {%- if dat.tuidcomp %} 12 | {%- set comma = joiner(', ') %} 13 | <{{ datel }} class="FileData"> 14 | {%- for val in dat.tuidcomp -%} 15 | {{ comma() }}{% if loop.first %}IFDB {% endif %}comp 16 | {%- endfor %} 17 | {%- endif %} 18 | 19 | {%- if dat.ifwiki %} 20 | {%- set comma = joiner(', ') %} 21 | <{{ datel }} class="FileData">IFWiki: 22 | {% for val in dat.ifwiki -%} 23 | {{ comma() }}{{ val }} 24 | {%- endfor %} 25 | {%- endif %} 26 | 27 | {%- if dat.safety %} 28 | {%- set comma = joiner(', ') %} 29 | <{{ datel }} class="FileData">Content: 30 | {% for val in dat.safety|splitflatten -%} 31 | {%- set taglabel, tagdesc = tag_table.get(val, (val, 'Explanation not available')) -%} 32 | {{ comma() }}{{ taglabel }} 33 | {%- endfor %} 34 | {%- endif %} 35 | 36 | -------------------------------------------------------------------------------- /lib/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% if title %}{{ title }}{% else %}IF Archive{% endif %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | {% block header %} 19 |
20 |

{% if title %}{{ title }}{% else %}IF Archive{% endif %}

21 |
22 | {% endblock %} 23 | 24 |
25 | 26 |
27 | {% block description %} 28 | {% endblock %} 29 |
30 | 31 |
32 | {% block body %} 33 | {% endblock %} 34 |
35 | 36 |
37 | 38 | {% include "footer.html" %} 39 | 40 |
41 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # To run: 4 | # python3 tests.py 5 | 6 | # This used to have a lot of tests for the Template and ParamFile classes. 7 | # But we've gotten rid of those. So this is pretty vestigial. 8 | 9 | import unittest 10 | 11 | from ifmap import is_string_nonwhite 12 | from ifmap import escape_html_string 13 | 14 | class TestEscapeFunctions(unittest.TestCase): 15 | 16 | def test_escape_html_string(self): 17 | self.assertEqual(escape_html_string('foo'), 'foo') 18 | self.assertEqual(escape_html_string('foo&'), 'foo<i>&') 19 | self.assertEqual(escape_html_string('w x\ny\tz'), 'w x\ny\tz') 20 | self.assertEqual(escape_html_string('x\x01y'), 'xy') 21 | self.assertEqual(escape_html_string('© αβγδε “”'), '© αβγδε “”') 22 | 23 | def test_is_string_nonwhite(self): 24 | self.assertIs(is_string_nonwhite(''), False) 25 | self.assertIs(is_string_nonwhite(' '), False) 26 | self.assertIs(is_string_nonwhite('\n \t \n'), False) 27 | self.assertIs(is_string_nonwhite('x'), True) 28 | self.assertIs(is_string_nonwhite('x \n'), True) 29 | self.assertIs(is_string_nonwhite('\n x'), True) 30 | 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | 35 | -------------------------------------------------------------------------------- /testdata/blocktag.map: -------------------------------------------------------------------------------- 1 | # File blocking tags for the archiveblock plugin 2 | # See: https://github.com/iftechfoundation/ifarchive-archiveblock 3 | 4 | # This file is generated from the "safety" tags in the Index files. 5 | # Last generated: Thu, 15 Nov 2018 12:00:00 +0000 6 | 7 | # Each line represents a file or directory which is blocked in the UK. 8 | # A pathname that ends with "/*" tags all the files in that directory. 9 | # A pathname that ends with "/**" tags all the files in that directory *and* 10 | # its subdirectories. 11 | 12 | # The next field is separated from the pathname by a tab. The field has 13 | # the form "FLAGS:TAGS". The FLAGS is empty or "u" (meaning "block this 14 | # file in the UK"). The TAGS are a comma-separated list of values; 15 | # see https://ifarchive.org/misc/org-procedures.html. 16 | 17 | /if-archive/unprocessed/** u:unknown 18 | /if-archive/comp/files/all.zip u:visual-porn, visual-gore 19 | /if-archive/games/all.zip u:visual-porn, visual-gore 20 | /if-archive/games/game1 :text-porn 21 | /if-archive/games/old/older/game1-pre u:self-harm 22 | /if-archive/tagtest/** u:unknown 23 | /if-archive/tagtest/dir1/* :text-porn 24 | /if-archive/tagtest/dir1/sub2/** : 25 | /if-archive/tagtest/dir2/** : 26 | /if-archive/tagtest/dir3/* : 27 | /if-archive/tagtest/dir3/** u:unknown 28 | /if-archive/tagtest/file-porn u:visual-porn 29 | /if-archive/tagtest/file-safe : 30 | -------------------------------------------------------------------------------- /make-master-index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This constructs the Master-Index file (the plain text one, not the XML). 5 | It works by walking the directory tree, extracting all the Index files, 6 | and concatenating them together. It adds a # directory header at the top 7 | of each Index file, and increases the # header depth (of files, etc) by 8 | one. 9 | 10 | Usage: make-master-index.py [ htdocs ] 11 | 12 | The argument is optional, and defaults to /var/ifarchive/htdocs. 13 | 14 | (This used to read the ls-lR file, but not any more.) 15 | 16 | In normal Archive operation: 17 | 18 | /var/ifarchive/bin/make-master-index.py > if-archive/Master-Index 19 | 20 | """ 21 | 22 | import sys 23 | import os, os.path 24 | import re 25 | 26 | rootdir = '/var/ifarchive/htdocs' 27 | if len(sys.argv) >= 2: 28 | rootdir = sys.argv[1] 29 | 30 | rootarchdir = os.path.join(rootdir, 'if-archive') 31 | 32 | dirre = re.compile('^if-archive.*:$') 33 | 34 | for (dirpath, dirnames, filenames) in os.walk(rootarchdir): 35 | if 'Index' in filenames: 36 | currentdir = os.path.relpath(dirpath, start=rootdir) 37 | basename = (currentdir + ':') 38 | print() 39 | print('# ' + basename) 40 | filename = (rootdir + '/' + currentdir + '/Index') 41 | fl = open(filename, 'r', encoding='utf-8') 42 | for subln in fl.readlines(): 43 | if subln.startswith('#'): 44 | subln = '#'+subln 45 | print(subln, end='') 46 | fl.close() 47 | str = None 48 | print() 49 | print('------------------------------------------------------') 50 | 51 | # Ensure that we visit in Unicode sort order (not case-folded). 52 | dirnames.sort() 53 | 54 | -------------------------------------------------------------------------------- /aws-put.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # aws-put.py: Simple script to push a file to AWS. 4 | 5 | import sys 6 | import os.path 7 | import optparse 8 | import configparser 9 | 10 | import boto3 11 | 12 | popt = optparse.OptionParser(usage='aws-put.py filename bucket') 13 | 14 | popt.add_option('--stdin', 15 | action='store_true', dest='stdin', 16 | help='upload data streamed from stdin (the filename is still needed for the S3 side)') 17 | 18 | popt.add_option('--config', 19 | action='store', dest='config', metavar='PATH', 20 | help='config file (default: $HOME/.aws/credentials)') 21 | 22 | (opts, args) = popt.parse_args() 23 | 24 | if len(args) < 2: 25 | print('usage: aws-put.py filename bucket') 26 | sys.exit(0) 27 | 28 | filename = args[0] 29 | bucket = args[1] 30 | 31 | cliargs = {} 32 | 33 | if opts.config: 34 | config = configparser.ConfigParser() 35 | config.read(opts.config) 36 | if config.has_section('AWSStorage'): 37 | map = config['AWSStorage'] 38 | elif config.has_section('default'): 39 | map = config['default'] 40 | else: 41 | raise Exception('config file does not have credentials') 42 | cliargs['aws_access_key_id'] = map['aws_access_key_id'] 43 | cliargs['aws_secret_access_key'] = map['aws_secret_access_key'] 44 | 45 | s3client = boto3.client('s3', **cliargs) 46 | 47 | if opts.stdin: 48 | destfile = os.path.basename(filename) 49 | print('Uploading stdin as %s...' % (destfile,)) 50 | infl = sys.stdin.buffer 51 | else: 52 | destfile = os.path.basename(filename) 53 | print('Uploading file as %s...' % (destfile,)) 54 | infl = open(filename, 'rb') 55 | 56 | res = s3client.upload_fileobj(infl, bucket, destfile) 57 | 58 | if res is not None: 59 | print(res) 60 | 61 | -------------------------------------------------------------------------------- /make-master-index: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The top-level script to regenerate Master-Index. Also the ls-lR listing. 4 | # (Run this as root, please.) 5 | 6 | # Ensure that ls -lR sorts its output without any clever case-folding. 7 | export LC_COLLATE=C 8 | 9 | # Protect this script from running if it (or build-indexes) is already 10 | # running. (It will fail if build.lock already exists.) 11 | set -o noclobber 12 | if { > /var/ifarchive/htdocs/build.lock ; } then 13 | : 14 | else 15 | echo 'Build script is already running' 16 | ls -l /var/ifarchive/htdocs/build.lock 17 | exit 18 | fi 19 | set +o noclobber 20 | 21 | echo 'Building Master-Index...' 22 | 23 | # Create ls-lR and Master-Index. 24 | 25 | cd /var/ifarchive/htdocs 26 | #cp /var/ifarchive/ifnews if-archive/new-since-last-post 27 | ls -lRn if-archive > if-archive/ls-lR 28 | /var/ifarchive/bin/make-master-index.py > if-archive/Master-Index 29 | 30 | # Make sure those files have the correct group and mode. 31 | 32 | #chgrp ifarchive if-archive/{new-since-last-post,ls-lR,Master-Index} 33 | #chmod 664 if-archive/{new-since-last-post,ls-lR,Master-Index} 34 | chgrp ifarchive if-archive/{ls-lR,Master-Index} 35 | chmod 664 if-archive/{ls-lR,Master-Index} 36 | 37 | # Also set the group for all Index files, so that they're editable 38 | # through the admin portal. Same goes for directories, so that the admin 39 | # portal can move files around. 40 | # (Except we skip directories which are mode 700. This is the special 41 | # "lost+found" directory at the top level. 42 | # The Archive's files themselves (other than Index) are *not* editable 43 | # through the admin portal. 44 | 45 | cd /var/ifarchive/htdocs/if-archive 46 | find . -type f -name Index -print0 | xargs -0 chgrp uploaders 47 | find . -type f -name Index -print0 | xargs -0 chmod 664 48 | find . -type d -not -perm 700 -print0 | xargs -0 chgrp uploaders 49 | find . -type d -not -perm 700 -print0 | xargs -0 chmod 775 50 | 51 | # Clean up the lock file. 52 | 53 | rm /var/ifarchive/htdocs/build.lock 54 | -------------------------------------------------------------------------------- /lib/datelist.html: -------------------------------------------------------------------------------- 1 | {% extends "page.html" %} 2 | {% from 'macros.html' import wbrslash %} 3 | 4 | {% block description %} 5 |

6 | {% if interval %} 7 | This page lists the most recent {{ interval }} of files in the Archive, 8 | sorted by date. 9 | {% else %} 10 | This page lists every file in the Archive, sorted by date. 11 | {% endif %} 12 | (The last update of the file, not the date it first entered the 13 | Archive.) Note that this list is based on filesystem timestamps, 14 | which may be inaccurate – or just plain wrong – 15 | for various reasons. 16 |

17 | 18 | {% include "colindex.html" %} 19 | 20 |

21 | About the IF-Archive. 22 |

23 | 24 |

25 | List of directories. 26 |

27 | 28 |

29 | An RSS feed of recent files. 30 |

31 | 32 |

33 | Or, 34 | enter the Archive. 35 |

36 | {% endblock %} 37 | 38 | {% block body %} 39 | 40 | {% if not _files %} 41 |

There have been no new uploads in the past {{ interval }}.

42 | {% endif %} 43 | 44 |
45 | {% set parity = cycler("Even", "Odd") %} 46 | {% for file in _files %} 47 |
[{{ file.datestr }}] 48 | {{ wbrslash(file.dir) }}/{{ file.name }} 49 | {%- if showdetails %} 50 | {%- if file.hasunboxlink %} 51 |
View contents 52 | {%- endif %} 53 | {%- if file._metadata %} 54 | {% set dat = file._metadata %} {% set datel = 'dd' %} 55 | {% include "metadata.html" %} 56 | {%- endif %} 57 | {%- if file.hasdesc %} 58 |
{% autoescape false %}{{ file.desc }}{% endautoescape %} 59 | {%- endif %} 60 | {%- endif %} 61 | {% endfor %} 62 |
63 | 64 | {% endblock %} 65 | -------------------------------------------------------------------------------- /lib/xmlbase.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% for dir in _dirs %} 5 | 6 | 7 | {{ dir.dir }} 8 | {{ dir.parentdir }} 9 | {% if dir.count %}{{ dir.count }}{% else %}0{% endif %} 10 | {% if dir.subdircount %}{{ dir.subdircount }}{% else %}0{% endif %} 11 | {%- if dir.date %} 12 | {{ dir.datestr }} 13 | {{ dir.date }} 14 | {%- endif %} 15 | {%- if dir._metadata %} 16 | 17 | {%- for key, valls in dir._metadata %} 18 | {{ key }} 19 | {%- for val in valls %} 20 | {{ val }} 21 | {%- endfor %} 22 | 23 | {%- endfor %} 24 | 25 | {%- endif %} 26 | {%- if dir.hasxmldesc and dir.xmlheader %} 27 | 28 | {{ dir.xmlheader }} 29 | {%- endif %} 30 | {%- for key, val in dir._parentdescs %} 31 | 32 | {{ val }} 33 | {%- endfor %} 34 | 35 | 36 | {% for file in dir._files %} 37 | 38 | {{ file.name }} 39 | {{ file.dir }} 40 | {{ file.dir }}/{{ file.name }} 41 | {%- if file.filesize %} 42 | {{ file.filesize }} 43 | {%- endif %} 44 | {%- if file.date %} 45 | {{ file.datestr }} 46 | {{ file.date }} 47 | {%- endif %} 48 | {%- if file.md5 %} 49 | {{ file.md5 }} 50 | {%- endif %} 51 | {%- if file.sha512 %} 52 | {{ file.sha512 }} 53 | {%- endif %} 54 | {%- if file.islinkfile %} 55 | 56 | {{ file.linkpath }} 57 | 58 | {%- endif %} 59 | {%- if file.islinkdir %} 60 | 61 | {{ file.linkdir }} 62 | 63 | {%- endif %} 64 | {%- if file._metadata %} 65 | 66 | {%- for key, valls in file._metadata %} 67 | {{ key }} 68 | {%- for val in valls %} 69 | {{ val }} 70 | {%- endfor %} 71 | 72 | {%- endfor %} 73 | 74 | {%- endif %} 75 | {%- if file.hasxmldesc %} 76 | 77 | {{ file.xmldesc }} 78 | {%- endif %} 79 | {%- for key, val in file._parentdescs %} 80 | 81 | {{ val }} 82 | {%- endfor %} 83 | 84 | {% endfor %} 85 | 86 | {% endfor %} 87 | 88 | 89 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir2

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
    54 | 55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 | 66 |

1 File

67 |
68 | 69 |
file 70 | 71 | 72 | [14-Jul-2017] 73 | 74 | 75 |
76 | 77 | 78 |
79 | 80 |
81 | 82 | 97 | 98 | 99 |
100 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/space dir/"ascquot"/dir/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/space dir/"ascquot"/dir 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 60 |

1 File

61 |
62 | 63 |
"file" 64 | 65 | 66 | [20-Oct-2018] 67 | 68 | 69 |
70 | 71 | 72 |
73 | 74 |
75 | 76 | 91 | 92 | 93 |
94 | -------------------------------------------------------------------------------- /lib/main.html: -------------------------------------------------------------------------------- 1 | {% extends "page.html" %} 2 | {% from 'macros.html' import wbrslash %} 3 | {% from 'macros.html' import wbrlinkslash %} 4 | 5 | {% block header %} 6 | 7 |
8 |

Index: {{ wbrlinkslash(_dirlinkels) }}

9 |
10 | 11 |
12 | 13 | 19 | 20 | {% if parentdir %} 21 | 28 | {% endif %} 29 | 30 |
31 | 32 | {% endblock %} 33 | 34 | {% block description %} 35 | 36 | {%- autoescape false %} 37 | {{ header }} 38 | {%- endautoescape %} 39 | 40 | {%- if _metadata %} 41 | {% set dat = _metadata %} {% set datel = 'li' %} 42 |
    43 | {% include "metadata.html" %} 44 |
45 | {%- endif %} 46 | {%- if _dirsafety %} 47 | {% include "dirsafety.html" %} 48 | {%- endif %} 49 | 50 |
51 | 52 | {% endblock %} 53 | 54 | {% block body %} 55 | 56 | {%- if subdircount %} 57 |

{{ subdircount }} Subdirector{{ subdircount|pluralize('y', 'ies') }}

58 |
59 | {% set parity = cycler("Even", "Odd") %} 60 | {% for subdir in _subdirs %} 61 | {%- include "subdirentry.html" %} 62 | {% endfor %} 63 |
64 | {%- endif %} 65 | 66 | {%- if alsosubdircount %} 67 | {%- if subdircount %} 68 |

See Also

69 | {% else %} 70 |

See Also Subdirector{{ alsocount|pluralize('y', 'ies') }}

71 | {%- endif %} 72 |
73 | {% set parity = cycler("Even", "Odd") %} 74 | {% for subdir in _alsosubdirs %} 75 | {%- include "subdirentry.html" %} 76 | {% endfor %} 77 |
78 | {%- endif %} 79 | 80 | {%- if count %} 81 |

{{ count }} File{{ count|pluralize }}

82 |
83 | {% set parity = cycler("Even", "Odd") %} 84 | {% for file in _files %} 85 | {%- include "fileentry.html" %} 86 | {% endfor %} 87 |
88 | {%- endif %} 89 | 90 | {%- if alsocount %} 91 | {%- if count %} 92 |

See Also

93 | {% else %} 94 |

See Also File{{ alsocount|pluralize }}

95 | {%- endif %} 96 |
97 | {% set parity = cycler("Even", "Odd") %} 98 | {% for file in _alsofiles %} 99 | {%- include "fileentry.html" %} 100 | {% endfor %} 101 |
102 | {%- endif %} 103 | 104 | {% endblock %} 105 | 106 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir1/sub2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir1/sub2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir1/sub2

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
    54 | 55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 | 66 |

1 File

67 |
68 | 69 |
file 70 | 71 | 72 | [14-Jul-2017] 73 | 74 | 75 |
76 | 77 | 78 |
79 | 80 |
81 | 82 | 97 | 98 | 99 |
100 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir1/sub1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir1/sub1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir1/sub1

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 |

56 | This directory has not been reviewed for content warnings. 57 | Unless specifically noted, all files may have content of any type. 58 |

59 | 60 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | 69 |

1 File

70 |
71 | 72 |
file 73 | 74 | 75 | [14-Jul-2017] 76 | 77 | 78 |
79 | 80 | 81 |
82 | 83 |
84 | 85 | 100 | 101 | 102 |
103 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir3/sub1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir3/sub1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir3/sub1

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 |

56 | This directory has not been reviewed for content warnings. 57 | Unless specifically noted, all files may have content of any type. 58 |

59 | 60 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | 69 |

1 File

70 |
71 | 72 |
file 73 | 74 | 75 | [14-Jul-2017] 76 | 77 | 78 |
79 | 80 | 81 |
82 | 83 |
84 | 85 | 100 | 101 | 102 |
103 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir3

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
    54 | 55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 | 66 |

1 Subdirectory

67 |
68 | 69 |
sub1 70 | 71 | 72 | 73 | 74 |
75 |

1 File

76 |
77 | 78 |
file 79 | 80 | 81 | [14-Jul-2017] 82 | 83 | 84 |
85 | 86 | 87 |
88 | 89 |
90 | 91 | 106 | 107 | 108 |
109 | -------------------------------------------------------------------------------- /testdata/indexes/datev_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past week) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past week)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent week of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 |

There have been no new uploads in the past week.

82 | 83 | 84 |
85 | 86 | 87 |
88 | 89 | 90 |
91 | 92 |
93 | 94 | 109 | 110 | 111 |
112 | -------------------------------------------------------------------------------- /testdata/indexes/date_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past week) (names only) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past week) (names only)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent week of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 |

There have been no new uploads in the past week.

82 | 83 | 84 |
85 | 86 | 87 |
88 | 89 | 90 |
91 | 92 |
93 | 94 | 109 | 110 | 111 |
112 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/space dir/"ascquot"/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/space dir/"ascquot" 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 60 |

1 Subdirectory

61 |
62 | 63 |
dir 64 | 65 | 66 | 67 | 68 |
69 |

See Also File

70 |
71 | 72 |
dir/"file" 73 | 74 | 75 |

Deep description.

76 | 77 | 78 |
79 | 80 | 81 |
82 | 83 |
84 | 85 | 100 | 101 | 102 |
103 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/comp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/comp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/comp

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 60 |

1 Subdirectory

61 |
62 | 63 |
files 64 | 65 | 66 | 67 | 68 |
69 |

2 Files

70 |
71 | 72 |
leftfoot.blorb 73 | 74 | 75 | [20-Oct-2018] 76 | 77 |
rightfoot.blorb 78 | 79 | 80 | [20-Oct-2018] 81 | 82 | 83 |
84 | 85 | 86 |
87 | 88 |
89 | 90 | 105 | 106 | 107 |
108 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/dir1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest/dir1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest/dir1

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
    54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 |

62 | This directory has the following content warning: 63 | pornographic text. 64 |

65 | 66 | 67 | 68 |
69 | 70 | 71 |
72 | 73 |
74 | 75 |

2 Subdirectories

76 |
77 | 78 |
sub1 79 | 80 | 81 | 82 |
sub2 83 | 84 | 85 | 86 | 87 |
88 |

1 File

89 |
90 | 91 |
file 92 | 93 | 94 | [14-Jul-2017] 95 | 96 | 97 |
98 | 99 | 100 |
101 | 102 |
103 | 104 | 119 | 120 | 121 |
122 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/games/old/older/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/games/old/older 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/games/old/older

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |

Directory description (in the directory).

52 | 53 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | 69 |

1 File

70 |
71 | 72 |
game1-pre 73 | 74 | 75 | [06-Oct-2018] 76 | 77 | 78 |
IFDB entry, entry 79 |
IFWiki: 80 | Game 81 |
Content: 82 | suicide or self-injury 83 | 84 | 85 |

Game description.

86 | 87 | 88 |
89 | 90 | 91 |
92 | 93 |
94 | 95 | 110 | 111 | 112 |
113 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/games/old/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/games/old 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/games/old

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 | 59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 |

1 Subdirectory

68 |
69 | 70 |
older 71 | 72 | 73 | 74 | 75 |
76 |

1 File

77 |
78 | 79 |
game1-v1 80 | 81 | 82 | [08-Oct-2018] 83 | 84 | 85 |
86 |

See Also

87 | 88 |
89 | 90 |
older/game1-pre 91 | 92 | 93 | 94 | 95 |
IFDB entry 96 | 97 | 98 |

Double-depth file reference (from lower down).

99 | 100 | 101 |
102 | 103 | 104 |
105 | 106 |
107 | 108 | 123 | 124 | 125 |
126 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/art/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/art 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/art

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |

Files from an art show: 52 | http://example.com/art/

53 | 54 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | 69 |

4 Files

70 |
71 | 72 |
concept.txt 73 | 74 | 75 |

The concept statement of the art show:

76 |

A description paragraph.

77 | 78 |
contents.txt 79 | 80 | 81 |

A table of contents

82 |
    83 |
  • Entry 1
  • 84 |
  • Entry 2
  • 85 |
  • Entry 3
  • 86 |
87 | 88 |
foo.z5 89 | 90 | 91 | [20-Oct-2018] 92 |

Foo: An Art Project 93 | Release 1 / Serial number 000000

94 | 95 |
x&all.zip 96 | 97 | 98 | [20-Oct-2018] 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 108 |
109 | 110 |
111 | 112 | 127 | 128 | 129 |
130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ifmap.py -- the index generator tool for the IF Archive 2 | 3 | - Copyright 2017-23 by the Interactive Fiction Technology Foundation 4 | - Distributed under the MIT license 5 | - Created by Andrew Plotkin 6 | 7 | This program has one core task: to look through all the files in the IF Archive, combine that with the contents of the Master-Index file, and generate all the index.html files in the indexes subdirectory. 8 | 9 | (The Master-Index file is created by sewing together all the Index files in all the directories of the Archive. That's done by the make-master-index.py script, also included in this repo.) 10 | 11 | Related files in this repo: 12 | 13 | - `make-master-index.py`: Sew together all the Index files to create Master-Index. 14 | - `make-master-index`: Shell script wrapper to run `make-master-index.py` on the Archive. 15 | - `build-indexes`: Shell script wrapper to run `ifmap.py` on the Archive. 16 | - `build-indexes-bg`: Another shell script wrapper to run both `make-master-index` and `build-indexes` sequentially in the background. (This is used by the admin web interface.) 17 | - `uncache.py`: Cloudflare API request to purge an Archive file from the CDN cache. 18 | - `nightly-backup`: Shell script wrapper for the `rclone` nightly backup to AWS. 19 | 20 | (Okay, some of these are only tangentially related. Miscellaneous admin scripts have accumulated here.) 21 | 22 | ## Setup 23 | 24 | The generator uses the Python Markdown and Jinja packages. To install these: 25 | 26 | ``` 27 | pip3 install -r requirements.txt 28 | ``` 29 | 30 | ## Arguments 31 | 32 | In normal Archive operation, this is invoked from the `build-indexes` script. 33 | 34 | - --index FILE: pathname of Master-Index. (Normally /var/ifarchive/htdocs/if-archive/Master-Index.) 35 | - --src DIR: Pathname of the directory full of HTML templates which control the appearance of the index files. (Normally /var/ifarchive/lib/ifmap.) 36 | - --tree DIR: Pathname of the root directory which the Archive serves. (Normally /var/ifarchive/htdocs.) 37 | - --v: If set, print verbose output. 38 | - --exclude: If set, files without index entries are excluded from index listings. (Normally *not* set.) 39 | - --dest DIR: Firectory to write index files (relative to --tree; default "indexes") 40 | - --meta DIR: Firectory to write metadata files (relative to --tree; default "metadata") 41 | 42 | The `--dest` and `--meta` arguments exist only for development testing. If you use any value other than the default ("indexes", "metadata"), the generated indexes won't properly link to anything. 43 | 44 | ## Testing 45 | 46 | Type `python3 tests.py` to run tests on the low-level string-escaping and templating code. 47 | 48 | For an end-to-end test, try: 49 | 50 | ./end-to-end-test 51 | 52 | If everything works, the generated files in testdata/indexes should match what's in the Git repository. (`git status` should show no changes.) 53 | 54 | ## History 55 | 56 | I wrote the first version of this program in 1999-ish. It was built around the original Index files, which were hand-written by Volker Blasius (the original Archive curator) for human consumption. Their format was not particularly convenient for parsing, but I parsed them anyway. 57 | 58 | I wrote the original program in C because it was portable and I didn't know Python or Perl yet. C is a terrible language for this sort of thing, of course -- I started by implementing my own hash tables. And escaping strings for HTML? Yuck. 59 | 60 | I finally ported it all to Python in July of 2017. It's now got less than half the lines of code, it's infinitely more readable, and it's faster. (Not because Python is faster, but because I added an MD5-caching feature.) 61 | 62 | December 2018: Added SHA512 checksums to the output (and to md5-cache.txt, which is now misnamed). Updated the script to generate foo/bar/index.html indexes as well as fooXbar.html indexes. 63 | 64 | December 2019: Renamed md5-cache.txt to checksum-cache.txt and moved it to the archive root. Simplified the arguments a bit (--dest is usually not needed). Updated everything to use the new Markdown-based Index format. 65 | 66 | October 2023: Directories can now have metadata, just like files. Support the "ifwiki" metadata line. 67 | 68 | November 2023: Generate the RSS feed along with all the other index files. 69 | 70 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/comp/files/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/comp/files 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/comp/files

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 60 |

4 Files

61 |
62 | 63 |
all.zip 64 | 65 | 66 | [16-Oct-2018] 67 |
View contents 68 | 69 | 70 |
Content: 71 | pornographic images, violent or gory images 72 | 73 | 74 | 75 |
[linked from if-archive/games/all.zip] 76 | 77 | 78 |
file-1.txt 79 | 80 | 81 | [14-Oct-2018] 82 | 83 | 84 |
IFWiki: 85 | File One 86 | 87 | 88 | 89 |
[linked from if-archive/compfile1] 90 | 91 | 92 |
file-2.txt 93 | 94 | 95 | [15-Oct-2018] 96 | 97 |
file-3.txt 98 | 99 | 100 | [16-Oct-2018] 101 | 102 | 103 |
104 | 105 | 106 |
107 | 108 |
109 | 110 | 125 | 126 | 127 |
128 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/tagtest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/tagtest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/tagtest

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
    54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 |

62 | This directory has not been reviewed for content warnings. 63 | Unless specifically noted, all files may have content of any type. 64 |

65 | 66 | 67 | 68 |
69 | 70 | 71 |
72 | 73 |
74 | 75 |

3 Subdirectories

76 |
77 | 78 |
dir1 79 | 80 | 81 | 82 |
dir2 83 | 84 | 85 | 86 |
dir3 87 | 88 | 89 | 90 | 91 |
92 |

3 Files

93 |
94 | 95 |
file 96 | 97 | 98 | [14-Jul-2017] 99 | 100 |
file-porn 101 | 102 | 103 | [14-Jul-2017] 104 | 105 | 106 |
Content: 107 | pornographic images 108 | 109 | 110 | 111 |
file-safe 112 | 113 | 114 | [14-Jul-2017] 115 | 116 | 117 |
Content: 118 | no warnings 119 | 120 | 121 | 122 | 123 |
124 | 125 | 126 |
127 | 128 |
129 | 130 | 145 | 146 | 147 |
148 | -------------------------------------------------------------------------------- /testdata/indexes/dirlist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Complete Index of Directories 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Complete Index of Directories

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | About the IF-Archive. 30 |

31 | 32 | 49 | 50 | 51 |

52 | Or, 53 | enter the Archive. 54 |

55 | 56 |
57 | 58 | 102 | 103 |
104 | 105 | 120 | 121 | 122 |
123 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 |
44 |

You feel somewhat disoriented as you pass through...

45 |
46 |

This is the Interactive Fiction Archive at 47 | ifarchive.org.

48 |

You may be interested in...

49 | 57 |

Or, go delving into the hierarchical tree shown here.

58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 | 66 |

6 Subdirectories

67 |
68 | 69 |
art 70 | 71 | 72 |

Art stuff.

73 | 74 |
comp 75 | 76 | 77 |

Competitions. This is a long line of text so we can see how the column width is going to work out in real life.

78 | 79 |
compfiles 80 | 81 | 82 | [link to if-archive/comp/files] 83 | 84 |
games 85 | 86 | 87 |

Games stuff.

88 | 89 |
space dir 90 | 91 | 92 | 93 |
tagtest 94 | 95 | 96 | 97 | 98 |
99 |

2 Files

100 |
101 | 102 |
compfile1 103 | 104 | 105 | [14-Oct-2018] 106 | [link to if-archive/comp/files/file-1.txt] 107 | 108 |
Master-Index 109 | 110 | 111 | [20-Oct-2018] 112 | 113 | 114 |
115 | 116 | 117 |
118 | 119 |
120 | 121 | 136 | 137 | 138 |
139 | -------------------------------------------------------------------------------- /testdata/indexes/dirmap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index of Directories 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Index of Directories

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | This is a somewhat abbreviated list of the directories on the Archive. 30 | Directories for individual competition games and outdated game versions 31 | have been omitted. 32 | For a really absolutely complete list of directories, 33 | see here. 34 |

35 | 36 |

37 | About the IF-Archive. 38 |

39 | 40 | 57 | 58 | 59 |

60 | Or, 61 | enter the Archive. 62 |

63 | 64 |
65 | 66 |
67 | 68 | 69 |
70 | 71 | 72 |
if-archive 73 | 74 |
if-archive/art 75 |

Art stuff.

76 | 77 |
if-archive/comp 78 |

Competitions. This is a long line of text so we can see how the column width is going to work out in real life.

79 | 80 |
if-archive/comp/files 81 | 82 |
if-archive/games 83 |

Games stuff.

84 | 85 |
if-archive/games/old/older 86 | 87 |
if-archive/space dir 88 | 89 |
if-archive/space dir/"ascquot" 90 | 91 |
if-archive/space dir/"ascquot"/dir 92 | 93 |
if-archive/tagtest 94 | 95 |
if-archive/tagtest/dir1 96 | 97 |
if-archive/tagtest/dir1/sub1 98 | 99 |
if-archive/tagtest/dir1/sub2 100 | 101 |
if-archive/tagtest/dir2 102 | 103 |
if-archive/tagtest/dir3 104 | 105 |
if-archive/tagtest/dir3/sub1 106 | 107 |
108 | 109 | 110 |
111 | 112 |
113 | 114 | 129 | 130 | 131 |
132 | -------------------------------------------------------------------------------- /testdata/if-archive/Master-Index: -------------------------------------------------------------------------------- 1 | 2 | # if-archive: 3 | 4 | > _You feel somewhat disoriented as you pass through..._ 5 | 6 | This is the Interactive Fiction Archive at 7 | [ifarchive.org](/). 8 | 9 | You may be interested in... 10 | 11 | - The [README file for the Archive](/if-archive/README). 12 | - The [list of directories](/indexes/dirmap.html) in the Archive. 13 | - New arrivals in the [past week](/indexes/datev_1.html), [past month](/indexes/datev_2.html), [past three months](/indexes/datev_3.html), or [past year](/indexes/datev_4.html). 14 | - An [RSS feed](/indexes/archive.rss) of recent arrivals. 15 | - A list of [every file in the archive](/indexes/date.html) (tens of thousands!) 16 | - [Master-Index.xml](/indexes/Master-Index.xml), which describes every file in the Archive (very large XML file!) 17 | 18 | Or, go delving into the hierarchical tree shown here. 19 | 20 | ## art 21 | 22 | Art stuff. 23 | 24 | ## comp 25 | 26 | Competitions. This is a long line of text so we can see how the column width is going to work out in real life. 27 | 28 | ## games 29 | 30 | Games stuff. 31 | 32 | ------------------------------------------------------ 33 | 34 | # if-archive/art: 35 | ifwiki: IF Art Show 36 | 37 | Files from an art show: 38 | 39 | 40 | ## concept.txt 41 | 42 | The concept statement of the art show: 43 | 44 | A description paragraph. 45 | 46 | ## contents.txt 47 | 48 | A table of contents 49 | 50 | - Entry 1 51 | - Entry 2 52 | - Entry 3 53 | 54 | ## foo.z5 55 | 56 | *Foo*: An Art Project 57 | Release 1 / Serial number 000000 58 | 59 | ## x&all.zip 60 | unbox-link: false 61 | 62 | 63 | ------------------------------------------------------ 64 | 65 | # if-archive/comp/files: 66 | ## all.zip 67 | safety: visual-porn, visual-gore 68 | 69 | ## file-1.txt 70 | ifwiki: File One 71 | 72 | 73 | ------------------------------------------------------ 74 | 75 | # if-archive/games: 76 | 77 | Hey: these are some game files. 78 | 79 | - A directory link: 80 | - And a file link: 81 | - And a file index link: or 82 | 83 | ## game1 84 | tuid: xyz111 85 | ifwiki: Adventure 86 | safety: text-porn 87 | 88 | Game number 1. 89 | 90 | ## game2 91 | tuid: xyz222 92 | 93 | ## bothgames 94 | tuid: xyz111 95 | xyz222 96 | 97 | ## all.zip 98 | 99 | Linked to comp/files. 100 | 101 | ## old 102 | tuid: abc12345 103 | 104 | The directory of old stuff. 105 | 106 | ## old/older 107 | tuid: def56789 108 | 109 | A double-depth directory. 110 | 111 | ## old/older/game1-pre 112 | tuid: ghi98765 113 | safety: self-harm 114 | 115 | Triple-depth file reference. 116 | 117 | ------------------------------------------------------ 118 | 119 | # if-archive/games/old: 120 | 121 | ## older/game1-pre 122 | tuid: ghij987654 123 | 124 | Double-depth file reference (from lower down). 125 | 126 | ------------------------------------------------------ 127 | 128 | # if-archive/games/old/older: 129 | ifwiki: Folder 130 | 131 | Directory description (in the directory). 132 | 133 | ## game1-pre 134 | ifwiki: Game 135 | 136 | Game description. 137 | 138 | ------------------------------------------------------ 139 | 140 | # if-archive/space dir: 141 | 142 | This directory tests funny filenames. 143 | 144 | Here are some index links: 145 | 146 | - 147 | - 148 | - 149 | - 150 | - 151 | - 152 | 153 | ## aaa1 154 | 155 | These first few are just for spacing. 156 | 157 | ## aaa2 158 | 159 | Test... 160 | 161 | ## aaa3 162 | 163 | Test... 164 | 165 | ## aaa4 166 | 167 | Test... 168 | 169 | ## αβγδε 170 | 171 | ## “author’s quotes” 172 | 173 | ## 😀 174 | 175 | ------------------------------------------------------ 176 | 177 | # if-archive/space dir/"ascquot": 178 | 179 | ## dir/"file" 180 | 181 | Deep description. 182 | 183 | 184 | ------------------------------------------------------ 185 | 186 | # if-archive/tagtest: 187 | blocktree: yes 188 | 189 | 190 | ## file 191 | 192 | ## file-safe 193 | safety: none 194 | 195 | ## file-porn 196 | safety: visual-porn 197 | 198 | ------------------------------------------------------ 199 | 200 | # if-archive/tagtest/dir1: 201 | blockdir: text-porn 202 | 203 | ## file 204 | 205 | ------------------------------------------------------ 206 | 207 | # if-archive/tagtest/dir1/sub1: 208 | 209 | ## file 210 | 211 | ------------------------------------------------------ 212 | 213 | # if-archive/tagtest/dir1/sub2: 214 | blocktree: no 215 | 216 | ## file 217 | 218 | ------------------------------------------------------ 219 | 220 | # if-archive/tagtest/dir2: 221 | blocktree: no 222 | 223 | ## file 224 | 225 | ------------------------------------------------------ 226 | 227 | # if-archive/tagtest/dir3: 228 | blocktree: yes 229 | blockdir: no 230 | 231 | ## file 232 | 233 | ------------------------------------------------------ 234 | 235 | # if-archive/tagtest/dir3/sub1: 236 | 237 | ## file 238 | 239 | ------------------------------------------------------ 240 | -------------------------------------------------------------------------------- /uncache.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # uncache.py: Wipe a URL from the CloudFlare cache. 4 | 5 | # The argument is anything that looks like a Archive file URL or URI. 6 | # You can specify any number of them. All of the following are equivalent, 7 | # and will purge the file "foo" for all our primary domains (ifarchive.org, 8 | # www.ifarchive.org, and mirror.ifarchive.org): 9 | # 10 | # foo 11 | # if-archive/foo 12 | # /if-archive/foo 13 | # http://ifarchive.org/if-archive/foo 14 | # https://ifarchive.org/if-archive/foo 15 | # 16 | # You can also specify a URL directly. It will not be normalized. 17 | # 18 | # -u http://ifarchive.org/misc/ifarchive.css 19 | 20 | import sys 21 | import os, os.path 22 | import re 23 | import optparse 24 | import json 25 | import configparser 26 | import hashlib 27 | import urllib.request 28 | import zipfile 29 | 30 | popt = optparse.OptionParser(usage='uncache.py') 31 | 32 | popt.add_option('-u', '--url', 33 | action='append', dest='urls', 34 | help='purge this URL as-is (do not treat it as a file under if-archive)') 35 | 36 | popt.add_option('-n', '--dryrun', 37 | action='store_true', dest='dryrun', 38 | help='show the URLs that will be purged, but don\'t call CloudFlare') 39 | 40 | popt.add_option('-z', '--zip', 41 | action='store_true', dest='zip', 42 | help='for zip files, also purge the Unbox content URLs') 43 | 44 | (opts, args) = popt.parse_args() 45 | 46 | if not args and not opts.urls: 47 | print('usage: uncache.py URIs...') 48 | sys.exit(0) 49 | 50 | # Read the configuration values from configpath. 51 | 52 | configpath = '/var/ifarchive/lib/cloudflare.config' 53 | confparse = configparser.ConfigParser() 54 | confparse.read(configpath) 55 | config = confparse['DEFAULT'] 56 | 57 | api_secret_key = config['api_secret_key'] 58 | zone_id = config['zone_id'] 59 | account_email = config['account_email'] 60 | archivedir = config.get('archivedir', '/var/ifarchive/htdocs/if-archive') 61 | 62 | MAXFILES = 16 63 | 64 | def path_to_hash(path): 65 | # Convert a path to a hash string. This algorithm follows Unbox; see: 66 | # https://github.com/iftechfoundation/ifarchive-unbox/blob/main/doc/spec.md 67 | bytes = hashlib.sha512(path.encode()).hexdigest()[0:12] 68 | ival = int(bytes, 16) 69 | alpha = '0123456789abcdefghijklmnopqrstuvwxyz' 70 | res = [] 71 | while ival: 72 | ch = alpha[ival % 36] 73 | ival = ival // 36 74 | res.insert(0, ch) 75 | while len(res) < 10: 76 | res.insert(0, '0') 77 | return ''.join(res) 78 | 79 | # Extract the URLs from the command-line arguments. 80 | urls = [] 81 | 82 | # Raw URLs are used directly. 83 | if opts.urls: 84 | urls.extend(opts.urls) 85 | 86 | # Figure out the URLs for file arguments, and normalize them. 87 | 88 | pat = re.compile('^http[s]?://[a-z.]*ifarchive[.]org/', re.IGNORECASE) 89 | 90 | prefixes = [ 91 | 'http://ifarchive.org/if-archive/', 92 | 'https://ifarchive.org/if-archive/', 93 | 'http://www.ifarchive.org/if-archive/', 94 | 'https://www.ifarchive.org/if-archive/', 95 | 'http://mirror.ifarchive.org/if-archive/', 96 | 'https://mirror.ifarchive.org/if-archive/', 97 | # All references to ukrestrict should be https: 98 | 'https://ukrestrict.ifarchive.org/if-archive/', 99 | # Don't need the http: version for unbox; it redirects. But the query param *does* need all versions. 100 | 'https://unbox.ifarchive.org/?url=/if-archive/', 101 | 'https://unbox.ifarchive.org/?url=http://ifarchive.org/if-archive/', 102 | 'https://unbox.ifarchive.org/?url=https://ifarchive.org/if-archive/', 103 | ] 104 | 105 | filenames = [] 106 | 107 | for val in args: 108 | match = pat.match(val) 109 | if match: 110 | val = val[ match.end() : ] 111 | if val.startswith('/'): 112 | val = val[ 1 : ] 113 | if val.startswith('if-archive/'): 114 | val = val[ 11 : ] 115 | filenames.append(val) 116 | 117 | for val in filenames: 118 | for prefix in prefixes: 119 | urls.append(prefix+val) 120 | 121 | if opts.zip: 122 | for val in filenames: 123 | path = os.path.join(archivedir, val) 124 | hash = path_to_hash(val) 125 | if not os.path.isfile(path): 126 | print('zip file not found:', path) 127 | continue 128 | try: 129 | with zipfile.ZipFile(path) as zipfl: 130 | for val in zipfl.namelist(): 131 | urls.append('https://unbox.ifarchive.org/%s/%s' % (hash, val,)) 132 | except Exception as ex: 133 | print('%s: %s' % (path, ex,)) 134 | 135 | # Got all the URLs. 136 | print('Purging %d urls:' % (len(urls),)) 137 | for val in urls: 138 | print(val) 139 | 140 | if opts.dryrun: 141 | sys.exit() 142 | 143 | cmd = 'purge_cache' 144 | requrl = 'https://api.cloudflare.com/client/v4/zones/%s/%s' % (zone_id, cmd) 145 | headers = { 146 | 'Content-Type': 'application/json', 147 | 'X-Auth-Key': api_secret_key, 148 | 'X-Auth-Email': account_email, 149 | } 150 | 151 | # Transmit the API request(s). 152 | 153 | try: 154 | urlsleft = list(urls) 155 | while urlsleft: 156 | sendurls, urlsleft = urlsleft[ : MAXFILES ], urlsleft[ MAXFILES : ] 157 | 158 | data = json.dumps({ 'files':sendurls }).encode() 159 | req = urllib.request.Request(requrl, method='POST', data=data, headers=headers) 160 | 161 | with urllib.request.urlopen(req) as res: 162 | dat = res.read() 163 | dat = json.loads(dat.decode()) 164 | print(res.getcode(), 'success:', dat.get('success')) 165 | 166 | except urllib.error.HTTPError as ex: 167 | dat = ex.fp.read() 168 | msg = dat.decode() 169 | print('%s: %s' % (ex, msg,)) 170 | except Exception as ex: 171 | print('%s' % (ex,)) 172 | 173 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/space dir/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/space dir 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/space dir

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |

This directory tests funny filenames.

52 |

Here are some index links:

53 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | 69 |

1 Subdirectory

70 |
71 | 72 |
"ascquot" 73 | 74 | 75 | 76 | 77 |
78 |

11 Files

79 |
80 | 81 |
aaa1 82 | 83 | 84 |

These first few are just for spacing.

85 | 86 |
aaa2 87 | 88 | 89 |

Test...

90 | 91 |
aaa3 92 | 93 | 94 |

Test...

95 | 96 |
aaa4 97 | 98 | 99 |

Test...

100 | 101 |
hello(...)?! 102 | 103 | 104 | [20-Oct-2018] 105 | 106 |
q&&&r 107 | 108 | 109 | [20-Oct-2018] 110 | 111 |
space file.txt 112 | 113 | 114 | [20-Oct-2018] 115 | 116 |
~&amp;<br> 117 | 118 | 119 | [20-Oct-2018] 120 | 121 |
αβγδε 122 | 123 | 124 | 125 |
“author’s quotes” 126 | 127 | 128 | 129 |
😀 130 | 131 | 132 | 133 | 134 |
135 | 136 | 137 |
138 | 139 |
140 | 141 | 156 | 157 | 158 |
159 | -------------------------------------------------------------------------------- /testdata/checksum-cache.txt: -------------------------------------------------------------------------------- 1 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/file-porn 2 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/file 3 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir2/file 4 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir3/file 5 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir3/sub1/file 6 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir1/file 7 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir1/sub1/file 8 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/dir1/sub2/file 9 | 0 1500000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/tagtest/file-safe 10 | 19 1540000000 c19cb4e6a3926c266da0343d2d0b5f8e e977d28b986de1b6d37f5da3893087fd718d778d02feee408dcfc61914fcc75c227e3a58b4d9ea662101e71b92337c3ab743ed4d702434f85e1a690ba63caea5 testdata/if-archive/art/foo.z5 11 | 487 1540000000 e4f08d6708151f1197d8e4728f1b8693 2b6c836d8f28e47cc5ff2ef3b96da60974e8c8e74b0300e6fad6959ba5a175ca86bd312142a23713fad2f1e6f35ca21aed41339e7720f4551bddd578d5fb42d8 testdata/if-archive/art/x&all.zip 12 | 4063 1540000000 8573978c71f5a81bea253cc63bd889ac 15c2fe5d10f325a2809b9a719e1408758480636464d1dd1d957bd9b3c40b84bfdaca7d691d96e1c8c55c5f0132d693b1d62d2bcbdf94cae52a54f89d607402c7 testdata/if-archive/Master-Index 13 | 25 1540000000 c40894f1e64fbb7813e1e6b233978570 3d7508fd0b9b95945aff73ed255d622821ad5da330d9987ccae6b2dbffca98aa7ad034d15e1356aa05abd4d98bb93b3b90c12ed3e37008d3c1f12ab07da6629d testdata/if-archive/comp/leftfoot.blorb 14 | 7 1539700000 65b17cb1d1308e8bead96db0f31125b5 9aabba937febaca4450ecc2ea915537d77e58ecdf8d394b52aa8671f28a63ed9d05ee9cc558d425b9933ce4e6c66e695f126548cb14da91232beb9cee586992e testdata/if-archive/comp/files/file-3.txt 15 | 7 1539600000 4345184361915e8d0592760a254ab488 0c075c1c1e76852368d43e6d14016737817d850adae86ae5835ff5798f17ddc8c73206cd8f7a9dd27715eb5cda6d5c4cf3ab1e3d4e7fdbd1648eb9e86f3058ac testdata/if-archive/comp/files/file-2.txt 16 | 7 1539500000 f6994894a975f34178b43db4062cece1 474be20011ed9c91c4f0bba91e505489140f0b60ba2e1966623934498ba15f0f0a4a90dc0f6fe0893b92c7fcd5513eed83172b049b2c4f0f8efe81b069575f44 testdata/if-archive/comp/files/file-1.txt 17 | 487 1539700000 e4f08d6708151f1197d8e4728f1b8693 2b6c836d8f28e47cc5ff2ef3b96da60974e8c8e74b0300e6fad6959ba5a175ca86bd312142a23713fad2f1e6f35ca21aed41339e7720f4551bddd578d5fb42d8 testdata/if-archive/comp/files/all.zip 18 | 28 1540000000 cde894d652c4918acf10b7b5d440f5f7 08131070051ab20d4c30afa6fa7359301d89609ee4f68df1acd6448b08d976da4a5c19d03e01c95b881c3a12bfa1a8263245ffbc844de78c3165ba243ab27bb1 testdata/if-archive/comp/rightfoot.blorb 19 | 0 1540000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/space dir/q&&&r 20 | 0 1540000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/space dir/"ascquot"/dir/"file" 21 | 20 1540000000 b2f57c0b2f38ff7ad928bbba01e3ecf4 5bca54049e9114f5f3cf22599b0688b9e1db23f81cfe0fe5d12a622209169024e3bc3871667d7fee5f6ae70139dd3bec291c8408ad61c2d5b2bb6adc5adedbe9 testdata/if-archive/space dir/space file.txt 22 | 0 1540000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/space dir/hello(...)?! 23 | 0 1540000000 d41d8cd98f00b204e9800998ecf8427e cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e testdata/if-archive/space dir/~&
24 | 8 1540000000 c91dbd63256ad50f86226fcd99737eaa 9a25e2e24dc1382f175ec5845019cd5fd8738f562d3164718d369cf8af8df30d428e7a2355769510bf61a7fc119fcdbd17fb3a401710b80319a6cdb9f6ad07e8 testdata/if-archive/games/game2 25 | 21 1539000000 de1b9390c9b8805c0c07b7ee78d2e75d 4f16a98f8de648ad54fd988e40b26eb83f31bb0c0377a821fb0579a1b59dd35b1f776d6526d851d1c62c7ca5b11227450edd7e243d646490ee3a2944c6b815f1 testdata/if-archive/games/old/game1-v1 26 | 21 1538800000 9ddfc5003ba50de6ab967c67a92a78f9 427f788dfb72c2878413c627d03bbd43de8e0bf0cff46347ebec7c0865c5abf1d7121d0c7e335bf41f1217abd4c07893d6219d6abecec29d91c6b2c901c9fdf7 testdata/if-archive/games/old/older/game1-pre 27 | 38 1540000000 ec5b9d72cb9e0852be6a95aca639c864 1be8bc652414f9429b9a35e0f7ca74bbf64f53f973c9e0a6e11915661b53db9ce1215b13af5bb9237e744758b3f897947ec25340eb914086cea2223aeeac24fa testdata/if-archive/games/bothgames 28 | 8 1540000000 0e3548092f60ba3f9e1f7306c942c1c3 ac18dc66e3aea6d122632b08dd633fdc40e1dc00b0950b63b093777e156ebd12fbc1b2e1d1b78e04b6fcd5179330818d6a606a776d033f7c98c343bdbe24abe7 testdata/if-archive/games/game1 29 | -------------------------------------------------------------------------------- /testdata/indexes/date_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past month) (names only) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past month) (names only)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent month of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 |
[20-Oct-2018] 92 | if-archive/art/foo.z5 93 | 94 |
[20-Oct-2018] 95 | if-archive/games/game1 96 | 97 |
[20-Oct-2018] 98 | if-archive/games/game2 99 | 100 |
[20-Oct-2018] 101 | if-archive/space dir/hello(...)?! 102 | 103 |
[20-Oct-2018] 104 | if-archive/comp/leftfoot.blorb 105 | 106 |
[20-Oct-2018] 107 | if-archive/space dir/q&&&r 108 | 109 |
[20-Oct-2018] 110 | if-archive/comp/rightfoot.blorb 111 | 112 |
[20-Oct-2018] 113 | if-archive/space dir/space file.txt 114 | 115 |
[20-Oct-2018] 116 | if-archive/art/x&all.zip 117 | 118 |
[20-Oct-2018] 119 | if-archive/space dir/~&amp;<br> 120 | 121 |
[16-Oct-2018] 122 | if-archive/comp/files/all.zip 123 | 124 |
[16-Oct-2018] 125 | if-archive/games/all.zip 126 | 127 |
[16-Oct-2018] 128 | if-archive/comp/files/file-3.txt 129 | 130 |
131 | 132 | 133 |
134 | 135 |
136 | 137 | 152 | 153 | 154 |
155 | -------------------------------------------------------------------------------- /testdata/indexes/if-archive/games/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index: if-archive/games 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 |

Index: if-archive/games

22 |
23 | 24 |
25 | 26 | 32 | 33 | 34 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |

Hey: these are some game files.

52 | 57 | 58 |
59 | 60 | 61 |
62 | 63 |
64 | 65 |

1 Subdirectory

66 |
67 | 68 |
old 69 | 70 | 71 | 72 | 73 |
IFDB entry 74 | 75 | 76 |

The directory of old stuff.

77 | 78 | 79 |
80 |

See Also

81 | 82 |
83 | 84 |
old/older 85 | 86 | 87 | 88 | 89 |
IFDB entry 90 | 91 | 92 |

A double-depth directory.

93 | 94 | 95 |
96 |

4 Files

97 |
98 | 99 |
all.zip 100 | 101 | 102 | [16-Oct-2018] 103 | [link to if-archive/comp/files/all.zip] 104 |
View contents 105 |

Linked to comp/files.

106 | 107 |
bothgames 108 | 109 | 110 | [20-Oct-2018] 111 | 112 | 113 |
IFDB entry, entry 114 | 115 | 116 | 117 |
game1 118 | 119 | 120 | [20-Oct-2018] 121 | 122 | 123 |
IFDB entry 124 |
IFWiki: 125 | Adventure 126 |
Content: 127 | pornographic text 128 | 129 | 130 |

Game number 1.

131 | 132 |
game2 133 | 134 | 135 | [20-Oct-2018] 136 | 137 | 138 |
IFDB entry 139 | 140 | 141 | 142 | 143 |
144 |

See Also

145 | 146 |
147 | 148 |
old/older/game1-pre 149 | 150 | 151 | 152 | 153 |
IFDB entry 154 |
Content: 155 | suicide or self-injury 156 | 157 | 158 |

Triple-depth file reference.

159 | 160 | 161 |
162 | 163 | 164 |
165 | 166 |
167 | 168 | 183 | 184 | 185 |
186 | -------------------------------------------------------------------------------- /testdata/indexes/archive.rss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IFArchive Recent Additions 6 | https://ifarchive.org 7 | New additions to the Interactive Fiction Archive 8 | en-us 9 | tech@ifarchive.org (Archive Support) 10 | Sat, 20 Oct 2018 01:46:40 +0000 11 | Thu, 15 Nov 2018 12:00:00 +0000 12 | 13 | 14 | 15 | "file" 16 | https://ifarchive.org/if-archive/space%20dir/%22ascquot%22/dir/%22file%22 17 | if-archive/space%20dir/%22ascquot%22/dir/%22file%22::1540000000 18 | Sat, 20 Oct 2018 01:46:40 +0000 19 | 20 | [at: if-archive/space%20dir/%22ascquot%22/dir/%22file%22] 21 | 22 | 23 | 24 | 25 | bothgames 26 | https://ifarchive.org/if-archive/games/bothgames 27 | if-archive/games/bothgames::1540000000 28 | Sat, 20 Oct 2018 01:46:40 +0000 29 | 30 | [at: if-archive/games/bothgames] 31 | 32 | 33 | 34 | 35 | foo.z5 36 | https://ifarchive.org/if-archive/art/foo.z5 37 | if-archive/art/foo.z5::1540000000 38 | Sat, 20 Oct 2018 01:46:40 +0000 39 | 40 | 41 | *Foo*: An Art Project 42 | Release 1 / Serial number 000000 43 | 44 | [at: if-archive/art/foo.z5] 45 | 46 | 47 | 48 | 49 | game1 50 | https://ifarchive.org/if-archive/games/game1 51 | if-archive/games/game1::1540000000 52 | Sat, 20 Oct 2018 01:46:40 +0000 53 | 54 | 55 | Game number 1. 56 | 57 | [at: if-archive/games/game1] 58 | 59 | 60 | 61 | 62 | game2 63 | https://ifarchive.org/if-archive/games/game2 64 | if-archive/games/game2::1540000000 65 | Sat, 20 Oct 2018 01:46:40 +0000 66 | 67 | [at: if-archive/games/game2] 68 | 69 | 70 | 71 | 72 | hello(...)?! 73 | https://ifarchive.org/if-archive/space%20dir/hello%28...%29%3F%21 74 | if-archive/space%20dir/hello%28...%29%3F%21::1540000000 75 | Sat, 20 Oct 2018 01:46:40 +0000 76 | 77 | [at: if-archive/space%20dir/hello%28...%29%3F%21] 78 | 79 | 80 | 81 | 82 | leftfoot.blorb 83 | https://ifarchive.org/if-archive/comp/leftfoot.blorb 84 | if-archive/comp/leftfoot.blorb::1540000000 85 | Sat, 20 Oct 2018 01:46:40 +0000 86 | 87 | [at: if-archive/comp/leftfoot.blorb] 88 | 89 | 90 | 91 | 92 | q&&&r 93 | https://ifarchive.org/if-archive/space%20dir/q%26%26%26r 94 | if-archive/space%20dir/q%26%26%26r::1540000000 95 | Sat, 20 Oct 2018 01:46:40 +0000 96 | 97 | [at: if-archive/space%20dir/q%26%26%26r] 98 | 99 | 100 | 101 | 102 | rightfoot.blorb 103 | https://ifarchive.org/if-archive/comp/rightfoot.blorb 104 | if-archive/comp/rightfoot.blorb::1540000000 105 | Sat, 20 Oct 2018 01:46:40 +0000 106 | 107 | [at: if-archive/comp/rightfoot.blorb] 108 | 109 | 110 | 111 | 112 | space file.txt 113 | https://ifarchive.org/if-archive/space%20dir/space%20file.txt 114 | if-archive/space%20dir/space%20file.txt::1540000000 115 | Sat, 20 Oct 2018 01:46:40 +0000 116 | 117 | [at: if-archive/space%20dir/space%20file.txt] 118 | 119 | 120 | 121 | 122 | x&all.zip 123 | https://ifarchive.org/if-archive/art/x%26all.zip 124 | if-archive/art/x%26all.zip::1540000000 125 | Sat, 20 Oct 2018 01:46:40 +0000 126 | 127 | [at: if-archive/art/x%26all.zip] 128 | 129 | 130 | 131 | 132 | ~&amp;<br> 133 | https://ifarchive.org/if-archive/space%20dir/~%26amp%3B%3Cbr%3E 134 | if-archive/space%20dir/~%26amp%3B%3Cbr%3E::1540000000 135 | Sat, 20 Oct 2018 01:46:40 +0000 136 | 137 | [at: if-archive/space%20dir/~%26amp%3B%3Cbr%3E] 138 | 139 | 140 | 141 | 142 | all.zip 143 | https://ifarchive.org/if-archive/comp/files/all.zip 144 | if-archive/comp/files/all.zip::1539700000 145 | Tue, 16 Oct 2018 14:26:40 +0000 146 | 147 | [at: if-archive/comp/files/all.zip] 148 | 149 | 150 | 151 | 152 | file-3.txt 153 | https://ifarchive.org/if-archive/comp/files/file-3.txt 154 | if-archive/comp/files/file-3.txt::1539700000 155 | Tue, 16 Oct 2018 14:26:40 +0000 156 | 157 | [at: if-archive/comp/files/file-3.txt] 158 | 159 | 160 | 161 | 162 | file-2.txt 163 | https://ifarchive.org/if-archive/comp/files/file-2.txt 164 | if-archive/comp/files/file-2.txt::1539600000 165 | Mon, 15 Oct 2018 10:40:00 +0000 166 | 167 | [at: if-archive/comp/files/file-2.txt] 168 | 169 | 170 | 171 | 172 | file-1.txt 173 | https://ifarchive.org/if-archive/comp/files/file-1.txt 174 | if-archive/comp/files/file-1.txt::1539500000 175 | Sun, 14 Oct 2018 06:53:20 +0000 176 | 177 | [at: if-archive/comp/files/file-1.txt] 178 | 179 | 180 | 181 | 182 | game1-pre 183 | https://ifarchive.org/if-archive/games/old/older/game1-pre 184 | if-archive/games/old/older/game1-pre::1538800000 185 | Sat, 06 Oct 2018 04:26:40 +0000 186 | 187 | 188 | Game description. 189 | 190 | [at: if-archive/games/old/older/game1-pre] 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /testdata/indexes/date_4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past year) (names only) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past year) (names only)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent year of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 |
[20-Oct-2018] 92 | if-archive/art/foo.z5 93 | 94 |
[20-Oct-2018] 95 | if-archive/games/game1 96 | 97 |
[20-Oct-2018] 98 | if-archive/games/game2 99 | 100 |
[20-Oct-2018] 101 | if-archive/space dir/hello(...)?! 102 | 103 |
[20-Oct-2018] 104 | if-archive/comp/leftfoot.blorb 105 | 106 |
[20-Oct-2018] 107 | if-archive/space dir/q&&&r 108 | 109 |
[20-Oct-2018] 110 | if-archive/comp/rightfoot.blorb 111 | 112 |
[20-Oct-2018] 113 | if-archive/space dir/space file.txt 114 | 115 |
[20-Oct-2018] 116 | if-archive/art/x&all.zip 117 | 118 |
[20-Oct-2018] 119 | if-archive/space dir/~&amp;<br> 120 | 121 |
[16-Oct-2018] 122 | if-archive/comp/files/all.zip 123 | 124 |
[16-Oct-2018] 125 | if-archive/games/all.zip 126 | 127 |
[16-Oct-2018] 128 | if-archive/comp/files/file-3.txt 129 | 130 |
[15-Oct-2018] 131 | if-archive/comp/files/file-2.txt 132 | 133 |
[14-Oct-2018] 134 | if-archive/compfile1 135 | 136 |
[14-Oct-2018] 137 | if-archive/comp/files/file-1.txt 138 | 139 |
[08-Oct-2018] 140 | if-archive/games/old/game1-v1 141 | 142 |
[06-Oct-2018] 143 | if-archive/games/old/older/game1-pre 144 | 145 |
146 | 147 | 148 |
149 | 150 |
151 | 152 | 167 | 168 | 169 |
170 | -------------------------------------------------------------------------------- /testdata/indexes/date_3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past three months) (names only) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past three months) (names only)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent three months of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 |
[20-Oct-2018] 92 | if-archive/art/foo.z5 93 | 94 |
[20-Oct-2018] 95 | if-archive/games/game1 96 | 97 |
[20-Oct-2018] 98 | if-archive/games/game2 99 | 100 |
[20-Oct-2018] 101 | if-archive/space dir/hello(...)?! 102 | 103 |
[20-Oct-2018] 104 | if-archive/comp/leftfoot.blorb 105 | 106 |
[20-Oct-2018] 107 | if-archive/space dir/q&&&r 108 | 109 |
[20-Oct-2018] 110 | if-archive/comp/rightfoot.blorb 111 | 112 |
[20-Oct-2018] 113 | if-archive/space dir/space file.txt 114 | 115 |
[20-Oct-2018] 116 | if-archive/art/x&all.zip 117 | 118 |
[20-Oct-2018] 119 | if-archive/space dir/~&amp;<br> 120 | 121 |
[16-Oct-2018] 122 | if-archive/comp/files/all.zip 123 | 124 |
[16-Oct-2018] 125 | if-archive/games/all.zip 126 | 127 |
[16-Oct-2018] 128 | if-archive/comp/files/file-3.txt 129 | 130 |
[15-Oct-2018] 131 | if-archive/comp/files/file-2.txt 132 | 133 |
[14-Oct-2018] 134 | if-archive/compfile1 135 | 136 |
[14-Oct-2018] 137 | if-archive/comp/files/file-1.txt 138 | 139 |
[08-Oct-2018] 140 | if-archive/games/old/game1-v1 141 | 142 |
[06-Oct-2018] 143 | if-archive/games/old/older/game1-pre 144 | 145 |
146 | 147 | 148 |
149 | 150 |
151 | 152 | 167 | 168 | 169 |
170 | -------------------------------------------------------------------------------- /testdata/indexes/datev_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past month) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past month)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent month of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 | 92 |
IFDB entry, entry 93 | 94 | 95 | 96 |
[20-Oct-2018] 97 | if-archive/art/foo.z5 98 |

Foo: An Art Project 99 | Release 1 / Serial number 000000

100 | 101 |
[20-Oct-2018] 102 | if-archive/games/game1 103 | 104 | 105 |
IFDB entry 106 |
IFWiki: 107 | Adventure 108 |
Content: 109 | pornographic text 110 | 111 | 112 |

Game number 1.

113 | 114 |
[20-Oct-2018] 115 | if-archive/games/game2 116 | 117 | 118 |
IFDB entry 119 | 120 | 121 | 122 |
[20-Oct-2018] 123 | if-archive/space dir/hello(...)?! 124 | 125 |
[20-Oct-2018] 126 | if-archive/comp/leftfoot.blorb 127 | 128 |
[20-Oct-2018] 129 | if-archive/space dir/q&&&r 130 | 131 |
[20-Oct-2018] 132 | if-archive/comp/rightfoot.blorb 133 | 134 |
[20-Oct-2018] 135 | if-archive/space dir/space file.txt 136 | 137 |
[20-Oct-2018] 138 | if-archive/art/x&all.zip 139 | 140 | 141 | 142 | 143 | 144 |
[20-Oct-2018] 145 | if-archive/space dir/~&amp;<br> 146 | 147 |
[16-Oct-2018] 148 | if-archive/comp/files/all.zip 149 |
View contents 150 | 151 | 152 |
Content: 153 | pornographic images, violent or gory images 154 | 155 | 156 | 157 |
[16-Oct-2018] 158 | if-archive/games/all.zip 159 |
View contents 160 |

Linked to comp/files.

161 | 162 |
[16-Oct-2018] 163 | if-archive/comp/files/file-3.txt 164 | 165 |
166 | 167 | 168 |
169 | 170 |
171 | 172 | 187 | 188 | 189 |
190 | -------------------------------------------------------------------------------- /testdata/indexes/date.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | All Files by Date (names only) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

All Files by Date (names only)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists every file in the Archive, sorted by date. 31 | 32 | (The last update of the file, not the date it first entered the 33 | Archive.) Note that this list is based on filesystem timestamps, 34 | which may be inaccurate – or just plain wrong – 35 | for various reasons. 36 |

37 | 38 | 55 | 56 | 57 |

58 | About the IF-Archive. 59 |

60 | 61 |

62 | List of directories. 63 |

64 | 65 |

66 | An RSS feed of recent files. 67 |

68 | 69 |

70 | Or, 71 | enter the Archive. 72 |

73 | 74 |
75 | 76 |
77 | 78 | 79 | 80 | 81 |
82 | 83 | 84 |
[20-Oct-2018] 85 | if-archive/space dir/"ascquot"/dir/"file" 86 | 87 |
[20-Oct-2018] 88 | if-archive/games/bothgames 89 | 90 |
[20-Oct-2018] 91 | if-archive/art/foo.z5 92 | 93 |
[20-Oct-2018] 94 | if-archive/games/game1 95 | 96 |
[20-Oct-2018] 97 | if-archive/games/game2 98 | 99 |
[20-Oct-2018] 100 | if-archive/space dir/hello(...)?! 101 | 102 |
[20-Oct-2018] 103 | if-archive/comp/leftfoot.blorb 104 | 105 |
[20-Oct-2018] 106 | if-archive/Master-Index 107 | 108 |
[20-Oct-2018] 109 | if-archive/space dir/q&&&r 110 | 111 |
[20-Oct-2018] 112 | if-archive/comp/rightfoot.blorb 113 | 114 |
[20-Oct-2018] 115 | if-archive/space dir/space file.txt 116 | 117 |
[20-Oct-2018] 118 | if-archive/art/x&all.zip 119 | 120 |
[20-Oct-2018] 121 | if-archive/space dir/~&amp;<br> 122 | 123 |
[16-Oct-2018] 124 | if-archive/comp/files/all.zip 125 | 126 |
[16-Oct-2018] 127 | if-archive/games/all.zip 128 | 129 |
[16-Oct-2018] 130 | if-archive/comp/files/file-3.txt 131 | 132 |
[15-Oct-2018] 133 | if-archive/comp/files/file-2.txt 134 | 135 |
[14-Oct-2018] 136 | if-archive/compfile1 137 | 138 |
[14-Oct-2018] 139 | if-archive/comp/files/file-1.txt 140 | 141 |
[08-Oct-2018] 142 | if-archive/games/old/game1-v1 143 | 144 |
[06-Oct-2018] 145 | if-archive/games/old/older/game1-pre 146 | 147 |
[14-Jul-2017] 148 | if-archive/tagtest/dir1/file 149 | 150 |
[14-Jul-2017] 151 | if-archive/tagtest/dir1/sub1/file 152 | 153 |
[14-Jul-2017] 154 | if-archive/tagtest/dir1/sub2/file 155 | 156 |
[14-Jul-2017] 157 | if-archive/tagtest/dir2/file 158 | 159 |
[14-Jul-2017] 160 | if-archive/tagtest/dir3/file 161 | 162 |
[14-Jul-2017] 163 | if-archive/tagtest/dir3/sub1/file 164 | 165 |
[14-Jul-2017] 166 | if-archive/tagtest/file 167 | 168 |
[14-Jul-2017] 169 | if-archive/tagtest/file-porn 170 | 171 |
[14-Jul-2017] 172 | if-archive/tagtest/file-safe 173 | 174 |
175 | 176 | 177 |
178 | 179 |
180 | 181 | 196 | 197 | 198 |
199 | -------------------------------------------------------------------------------- /testdata/indexes/datev_4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past year) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past year)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent year of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 | 92 |
IFDB entry, entry 93 | 94 | 95 | 96 |
[20-Oct-2018] 97 | if-archive/art/foo.z5 98 |

Foo: An Art Project 99 | Release 1 / Serial number 000000

100 | 101 |
[20-Oct-2018] 102 | if-archive/games/game1 103 | 104 | 105 |
IFDB entry 106 |
IFWiki: 107 | Adventure 108 |
Content: 109 | pornographic text 110 | 111 | 112 |

Game number 1.

113 | 114 |
[20-Oct-2018] 115 | if-archive/games/game2 116 | 117 | 118 |
IFDB entry 119 | 120 | 121 | 122 |
[20-Oct-2018] 123 | if-archive/space dir/hello(...)?! 124 | 125 |
[20-Oct-2018] 126 | if-archive/comp/leftfoot.blorb 127 | 128 |
[20-Oct-2018] 129 | if-archive/space dir/q&&&r 130 | 131 |
[20-Oct-2018] 132 | if-archive/comp/rightfoot.blorb 133 | 134 |
[20-Oct-2018] 135 | if-archive/space dir/space file.txt 136 | 137 |
[20-Oct-2018] 138 | if-archive/art/x&all.zip 139 | 140 | 141 | 142 | 143 | 144 |
[20-Oct-2018] 145 | if-archive/space dir/~&amp;<br> 146 | 147 |
[16-Oct-2018] 148 | if-archive/comp/files/all.zip 149 |
View contents 150 | 151 | 152 |
Content: 153 | pornographic images, violent or gory images 154 | 155 | 156 | 157 |
[16-Oct-2018] 158 | if-archive/games/all.zip 159 |
View contents 160 |

Linked to comp/files.

161 | 162 |
[16-Oct-2018] 163 | if-archive/comp/files/file-3.txt 164 | 165 |
[15-Oct-2018] 166 | if-archive/comp/files/file-2.txt 167 | 168 |
[14-Oct-2018] 169 | if-archive/compfile1 170 | 171 |
[14-Oct-2018] 172 | if-archive/comp/files/file-1.txt 173 | 174 | 175 |
IFWiki: 176 | File One 177 | 178 | 179 | 180 |
[08-Oct-2018] 181 | if-archive/games/old/game1-v1 182 | 183 |
[06-Oct-2018] 184 | if-archive/games/old/older/game1-pre 185 | 186 | 187 |
IFDB entry, entry 188 |
IFWiki: 189 | Game 190 |
Content: 191 | suicide or self-injury 192 | 193 | 194 |

Game description.

195 | 196 |
197 | 198 | 199 |
200 | 201 |
202 | 203 | 218 | 219 | 220 |
221 | -------------------------------------------------------------------------------- /testdata/indexes/datev_3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Files by Date (past three months) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 |

Files by Date (past three months)

21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

29 | 30 | This page lists the most recent three months of files in the Archive, 31 | sorted by date. 32 | 33 | (The last update of the file, not the date it first entered the 34 | Archive.) Note that this list is based on filesystem timestamps, 35 | which may be inaccurate – or just plain wrong – 36 | for various reasons. 37 |

38 | 39 | 56 | 57 | 58 |

59 | About the IF-Archive. 60 |

61 | 62 |

63 | List of directories. 64 |

65 | 66 |

67 | An RSS feed of recent files. 68 |

69 | 70 |

71 | Or, 72 | enter the Archive. 73 |

74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 |
[20-Oct-2018] 86 | if-archive/space dir/"ascquot"/dir/"file" 87 | 88 |
[20-Oct-2018] 89 | if-archive/games/bothgames 90 | 91 | 92 |
IFDB entry, entry 93 | 94 | 95 | 96 |
[20-Oct-2018] 97 | if-archive/art/foo.z5 98 |

Foo: An Art Project 99 | Release 1 / Serial number 000000

100 | 101 |
[20-Oct-2018] 102 | if-archive/games/game1 103 | 104 | 105 |
IFDB entry 106 |
IFWiki: 107 | Adventure 108 |
Content: 109 | pornographic text 110 | 111 | 112 |

Game number 1.

113 | 114 |
[20-Oct-2018] 115 | if-archive/games/game2 116 | 117 | 118 |
IFDB entry 119 | 120 | 121 | 122 |
[20-Oct-2018] 123 | if-archive/space dir/hello(...)?! 124 | 125 |
[20-Oct-2018] 126 | if-archive/comp/leftfoot.blorb 127 | 128 |
[20-Oct-2018] 129 | if-archive/space dir/q&&&r 130 | 131 |
[20-Oct-2018] 132 | if-archive/comp/rightfoot.blorb 133 | 134 |
[20-Oct-2018] 135 | if-archive/space dir/space file.txt 136 | 137 |
[20-Oct-2018] 138 | if-archive/art/x&all.zip 139 | 140 | 141 | 142 | 143 | 144 |
[20-Oct-2018] 145 | if-archive/space dir/~&amp;<br> 146 | 147 |
[16-Oct-2018] 148 | if-archive/comp/files/all.zip 149 |
View contents 150 | 151 | 152 |
Content: 153 | pornographic images, violent or gory images 154 | 155 | 156 | 157 |
[16-Oct-2018] 158 | if-archive/games/all.zip 159 |
View contents 160 |

Linked to comp/files.

161 | 162 |
[16-Oct-2018] 163 | if-archive/comp/files/file-3.txt 164 | 165 |
[15-Oct-2018] 166 | if-archive/comp/files/file-2.txt 167 | 168 |
[14-Oct-2018] 169 | if-archive/compfile1 170 | 171 |
[14-Oct-2018] 172 | if-archive/comp/files/file-1.txt 173 | 174 | 175 |
IFWiki: 176 | File One 177 | 178 | 179 | 180 |
[08-Oct-2018] 181 | if-archive/games/old/game1-v1 182 | 183 |
[06-Oct-2018] 184 | if-archive/games/old/older/game1-pre 185 | 186 | 187 |
IFDB entry, entry 188 |
IFWiki: 189 | Game 190 |
Content: 191 | suicide or self-injury 192 | 193 | 194 |

Game description.

195 | 196 |
197 | 198 | 199 |
200 | 201 |
202 | 203 | 218 | 219 | 220 |
221 | --------------------------------------------------------------------------------