A behavior that annoyed me for some time was that Eclipse disabled autobuild (“Project -> Build Automatically”) on startup, no matter whether it was active during a previous, regular shutdown of the workspace.

I was unable to find a suitable setting in the workspace settings.

Finally, it turned out that the described behavior can be turned off (and on), by modifying an Eclipse Oomph configuration file as follows:

  1. Navigate to HOME/.eclipse/org.eclipse.oomph.setup/setups .
  2. Open the XML file user.setup .
  3. Navigate to a setupTask XML element with the key /instance/org.eclipse.core.resources/description.autobuilding , and set the value to true.

In my settings file, the corresponding XML fragment looks like this:

<setupTask
  xsi:type="setup:CompoundTask"
  name="org.eclipse.core.resources">
  <setupTask
    xsi:type="setup:PreferenceTask"
    key="/instance/org.eclipse.core.resources/description.autobuilding"
    value="true"/>
  <setupTask
    xsi:type="setup:PreferenceTask"
    key="/instance/org.eclipse.core.resources/encoding"
    value="UTF-8"/>
  <setupTask
    xsi:type="setup:PreferenceTask"
    key="/instance/org.eclipse.core.resources/refresh.lightweight.enabled"
    value="true"/>
</setupTask>

 

 

By coincidence I recently found out how to open an Eclipse workspace via drag and drop on Windows.

It is actually a simple trick, but I want to share it anyway:

  1.  Create a link to the eclipse.exe that you want to use (e.g., named EclipseWorkspaceDragAndDrop on your Desktop).
  2. Open the properties of the link (e.g., via Alt+Enter).
  3. Locate the Target input field, whose content should end in eclipse.exe right now.
  4. At the very end of the input field, append -data.
  5. Open the Explorer (or suchlike), navigate to the parent directory of your workspace directory, and drag the workspace directory onto the link (e.g., EclipseWorkspaceDragAndDrop)
  6. Eclipse should startup into the selected workspace without asking you to choose one.

 

When using JUnit for creating unit tests, I have always felt annoyed that Eclipse keeps on suggesting (or even auto-importing) junit.framework.Assert, which is actually a deprecated type but appears first in the list of import suggestions.

To disable this behavior, one has to change the so-called Access Rules for the JUnit library.

To do this:

  1. Right-click you project and select Build Path -> Configure…
  2. Switch to Libraries tab and locate the used junit.jar
  3. Select Access rules and press Edit.
  4. Add a new rule with the rule pattern junit/framework/Assert and set its access level to Forbidden.
  5. Make sure that the rule appears before the junit/framework/* rule, which permits any acccess.

Now, Eclipse should only provide for one import quick fix, when using Assert. Even “Organize Imports” (Ctrl+Shift+O) should automatically find the correct solution now.

Thanks to this Stackoverflow post for hinting me at this solution.

The utility svnrdump allows you to dump and load Subversion repositories, including the whole revision history.

A typical use case for svnrdump looks like this:

svnrdump dump <OLD-REPOSITORY-URL>  > my-repository.dump
svnrdump load <NEW-REPOSITORY-URL> < my-reposiotory.dump

To find out which revisions a particular dump file contains, grep comes in handy: The contained revision numbers are stored in plain text and in increasing order in the dump file.

grep -a "Revision-number:" my-repository.dump

returns all stored revisions. To find out the first or last contained revision, simply use head and tail:

grep -a "Revision-number:" my-repository.dump | head -n 1
grep -a "Revision-number:" my-repository.dump | tail -n 1

 

In Git, every line of the file .gitignore is a regular expression that describes files that should be ignored. However, one can also add lines that state which files not to ignore.

Example:

The following example shows a configuration that ignores everything in a particular directory (./tmp) but explicitly states that PDF files in ./tmp should not be ignored:

# Ignore everything in ./tmp ...
/tmp/*

# ... but do not ignore PDF files in ./tmp
!/tmp/*.pdf

 

Working with UTF-8-encoded PHP files in web applications, a common, hard-to-track-down error is the following: “Headers already sent” or “Cannot modify header information“. This usually happens during a call to the function header(), which manipulates the HTTP header.

One reason for this is that the UTF-8 file starts with an invisible(!) byte order mark (BOM) consisting of the three bytes 0xEF,0xBB,0xBF. The BOM can be removed by opening the file in a suitbale text editor and unticking the Add Byte Order Mark (BOM) .option (or similar).

A more convenient way using sed is the following:

sed -i '1 s/^\xef\xbb\xbf//' utf8_file.txt

(-i enables in-place operation of sed; 1 denotes that one replacement should happen; ^ denotes the start of a line)

Example

Let’s consider a file consisting of two lines (‘A’, ‘B’) stored with the BOM:

<BOM>A
B

Investigating this file with the hex tool od, :

$ od -t c -t x1 testfile.txt

we obtain the following output:

0000000 357 273 277   A  \n   B  \n
         ef  bb  bf  41  0a  42  0a
0000007

The three BOM bytes are clearly visible.

After running

sed -i '1 s/^\xef\xbb\xbf//' testfile.txt

The output looks as follows, proving that the BOM is gone:

0000000   A  \n   B  \n
         41  0a  42  0a
0000004

References

Allgemeine Konfiguration

Generischer Befehl:

git config

Git speichert Einstellungen auf drei Ebenen:

  • –file (default): für das aktuelle Repository
  • –global: für den aktuellen Benutzer
  • –system: für das gesamte System

Die wichtigsten Funktionalitäten sind:

  • Einstellung abfragen:
    git config --global
    
  • Einstellung setzen (–replace-all, da sonst ein weiterer und kein neuer Wert für gespeichert wird):
    git config --global --replace-all 
    
  • Einstellungen abfragen:
    git config --global --list
    

     

Nützliche Optionen sind dabei (Git macht auch Vorschläge, wenn man TAB betätigt):

  • user.name: Benutzername für Commit-Nachrichten
  • user.e-mail: Mailadresse für Commit-Nachrichten
  • core.editor: Editor für Nachrichten oder längere Texte (bspw. nano oder [Vim])
  • alias: Setzt mal beispielsweise alias.co auf checkout, dann kann man einen Checkout von nun an auch neben dem ursprünglichen langen git checkout mit dem Kurzbefehl git co durchführen.

Repository initialisieren

  • Repository im aktuellen Verzeichnis neu erstellen:
    git init
    
  • Ein existierendes Repository ins aktuelle Verzeichnis klonen (–shared: auch andere Benutzer des Systems können darauf zugreifen):
    git clone --shared  .
    
  • Diese Seite wird von nun an den Namen origin tragen.

Hinzufügen, Entfernen und Anzeigen von Dateien

  • Datei file.txt zum Repository hinzufügen
    git add file.txt
    
  • Dies funktioniert auch für Verzeichnisse.
  • Git fügt keine leeren Verzeichnisse hinzu!
  • Der Befehl git add file.txt hat noch zwei weitere Funktionen:
  • # Stage: Falls die Datei file.txt in den nächsten Commit aufgenommen werden soll.
  • # Resolved: Falls die Datei file.txt einen Konflikt hatte und jetzt als gelöst (resolved) markiert werden soll.
  • Datei file.txt aus dem Repository löschen:
  • Datei file.txt auch vom Dateisystem löschen:
    git rm file.txt
    
  • Datei file.txt nicht vom Dateisystem löschen:
    git rm --cached file.txt
    
  • Alle aktuelle versionierten Dateien anzeigen:
    git ls-files
  • Aktuellen Status anzeigen (Was wird committed? Was wurde verändert?):
    git status
    
  • Mit der Option -v bekommt man die Differenz zum Original angezeigt.

Änderungen rückgängig machen

Das Äquivalent eines Reverts (svn revert file.txt) bei Subversion lautet:

git reset HEAD test.txt     # only necessary if file has been deleted
git checkout -- test.txt

Entferntes Repository

  • Änderungen holen (Äquivalent eines Updatesvn update– bei Subversion):
  • Falls man nur eine entfernte Seite hat, dann ist der Aufruf einfach:
    git pull
    
  • Ansonsten muss man die entfernte Seite mit Namen (origin) (und ggfs. sogar Branch – master – nennen):
    git pull origin master
    
  • Änderungen schreiben (Äquivalent eines Commitsvn commit– bei Subversion)
  • Falls man nur eine entfernte Seite hat, dann ist der Aufruf einfach:
    git push
    
  • Ansonsten muss man die entfernte Seite mit Namen (origin) (und ggfs. sogar Branch – master – nennen):
    git push origin master
    

Informationen über entfernte Repositories

  • Ausführliche Informationen über remote comp5lx mittels git remote show :
  • Option -n verhindert, dass der Remote angesprochen wird, ansonsten funktioniert die Anfrage nur, wenn man gerade online ist.
    $ git remote show comp5lx -n
    * remote comp5lx
     Fetch URL: ssh://comp5lx/var/git/repo.git
     Push URL: ssh://comp5lx/var/git/repo.git
     HEAD branch: (not queried)
     Remote branch: (status not queried)
     master
     Local ref configured for 'git push' (status not queried):
     (matching) pushes to (matching)

Nachträglich entfernte Repositories tracken

Es kann unter Umständen passieren, dass der aktuelle lokale Branch einen entfernten Branch origin/b_remote nicht trackt und man dies nachholen möchte. In diesem Fall kann man mit der Option -u beim Pushen den Upstream Branch aktualisieren:

git push -u origin b_remote

Siehe auch: hier

Gespeicherte Zwischenstände (Tags)

Git bietet anders als Subversion eine integrierte Verwaltung von Tags:

  • Alle Tags auflisten:
    git tag
    
  • Tag erstellen (-f erzwingt Überschreiben):
    git tag -a "Version_1.0"
    
  • Tag löschen:
    git tag -d "Version_1.0"
    

Zweige (Branches)

  • Alle Branches auflisten:
    git branch
    
  • Branch neuer_branch erstellen
    git checkout -b "neuer_branch"
    
  • Branch löschen:
    git checkout master          # Branch verlassen
    git branch -d "neuer_branch"
    
  • Änderungen aus dem Branch neuer_branch in den Branch master übernehmen:
    git checkout master      # Branch wechseln
    git merge "neuer_branch" # Änderungen integrieren
    

 

GitHub mit einem anderen Identity File verwenden

Normalerweise nimmt der SSH-Befehl an, dass der öffentliche Schlüssel unter ~/.ssh/id_rsa.pub zu finden ist.
Möchte man nun einen anderen Schlüssel verwenden, dann sind die folgenden Schritte nötig:

    • Füge eine Hostdefinition in die Datei ~/.ssh/config ein:
      Host github
        Port 22
        Hostname github.com
        User git
        IdentityFile ~/.ssh/github_key
      
    • Hinzufügen/Klonen der remote site:
git remote add origin ssh://github/RomeoKilo/repository

Diverses

  • Passwort temporär speichern (default: 900 Sekunden):
    git config credential.helper cache
    git config credential.helper 'cache --timeout=300'
    
  • Fast Forward deaktivieren für Merges in den Master:
    git config --global branch.master.mergeoptions  "--no-ff" 
    
  • Lokale Kopien von entfernten Branches löschen, falls diese nicht mehr existieren:
    git fetch -p origin
    
  • Übersicht über den Zustand des Repositories:
    gitk --all
    
  • Grafisch mit Git arbeiten:
    git gui
    
  • Branches löschen, dir zwar als remote/… gekennzeichnet sind, aber in Wirklichkeit nicht mehr auf dem jeweiligen Remote-Server existieren:
    git remote prune origin
    
  • Hübsche Bash-Prompt:
    export PS1='\[\033[01;32m\]\u\[\033[01;34m\] \w\[\033[31m\]$(__git_ps1 " (%s)")\[\033[01;34m\]$\[\033[00m\] '
    

git.config (Beispiel)

Nutzerspezifische Einstellungen werden in der Datei ‘~/.gitconfig’ gespeichert:

[user]
        name = Rüdiger Kluge
        email = ruediger.kluge@conelek.com
[core]
#        autocrlf = true
[alias]
        l = log --graph --pretty=oneline --abbrev-commit --decorate
        lt = log --topo-order --graph --pretty=oneline --abbrev-commit --decorate
[color]
        # turn on color
        diff = auto
        status = auto
        branch = auto
        interactive = auto
        ui = auto
[color "branch"]
        # good looking colors i copy/pasted from somewhere
        current = green bold
        local = green
        remote = red bold
[color "diff"]
        # good looking colors i copy/pasted from somewhere
        meta = yellow bold
        frag = magenta bold
        old = red bold
        new = green bold
[color "status"]
        # good looking colors i copy/pasted from somewhere
        added = green bold
        changed = yellow bold
        untracked = red
[push]
        # 'git push' should only do the current branch, not all
        default = current
[branch]
        # always setup 'git pull' to rebase instead of merge
        autosetuprebase = always
[alias]
        st = status
        ci = commit
        br = branch
        co = checkout
        df = diff
[branch "master"]
    mergeoptions = --no-ff

Links

 

Diese Seite enthält eine Aufstellung nützlicher Befehle für den vi/vim.
Der Befehl vimtutor führt einen sanft in die Bedienung des Vim ein.

= Modi des vim =

Meistbenutzte Modi des vim:

Normal-/Befehlsmodus

  • Anfangszustand des vim: Von ihm aus erreicht man alle anderen Modi.
  • Durch 2 x ESC von überall erreichbar.

Einfügemodus

  • Vom Normalmodus aus durch Eingabe des Zeichens i (insert).
  • Zum Normalmodus hin durch Drücken von ESC oder CTRL+C
  • Fast jedes eingegebene Zeichen wird in die Datei geschrieben (bis auf bspw. ESC)

Kommandozeilenmodus

  • Vom Normalmodus aus durch Eingabe von : (Doppelpunkt)
  • Zum Normalmodus hin durch Drücken von ESC.

Visueller Modus

Vom Normalmodus aus durch Eingabe von

  • v (normales Markieren),
  • V (zeilenweise markieren) oder
  • CTRL+V (blockweise).

= Basisbefehle =

Durch wiederholtes Drücken von ESC erreicht man immer wieder den Anfangs-/Befehlsmodus.

Bewegungsbefehle

Bewegen kann man den Cursor über die Pfeiltasten oder über hjkl, was etwas schneller, aber auch gewöhnungsbedürftig ist, da man in der Zehnfingerstellung verbleiben kann.

Den Zeilenanfang erreicht man mit ^ oder der Null 0, das Zeilenende mit $.

Die aktuelle Zeilenummer erhält man mit CTRL+G.
Den Dokumentenanfang erreicht man mit gg, das Dokumentenende mit G.
An eine beliebige Zeile, sagen wir 1023, kann man sich durch den folgende Befehl bewegen:

1023G

Das nächste Wort erreicht man mittels w (word), das vorige Wort mit b(back), zum Wortende” des aktuellen Wortes kommt man mittels e

          gg

         
          k
^,0  b     w,e   $
          j
         

          G 

Jedem dieser Bewegungsbefehle kann man einen Faktor voranstellen, der angibt, wie häufig die Operation ausgeführt werden soll: Bei der Eingabe von 3w bewegt man sich 3 Wörter vorwärts.

Einfügen

Ausgangspunkt ist der Normalmodus. Die folgenden Befehle wechseln in den Einfügemodus:

  • Befehl i (insert): Einfügen vor dem markierten Zeichen
  • Befehl a (append): Einfügen nach dem markierten Zeichen
  • Befehl O: Einfügen einer leeren Zeile vor der aktuellen Zeile
  • Befehl o: Einfügen einer leeren Zeile nach der aktuellen Zeile

Kopieren

Ausgangspunkt ist der Normalmodus.

  • Befehl yy (yank): Kopieren der aktuellen Zeile (danach mit p einfügen)

Löschen

Das Löschkommando setzt sich aus dem Löschoperator d und einer Bewegungsrichtung zusammen; man kann alle oben erwähnten Bewegungsbefehle (^,$,e,w,2w,b,3b,…) nutzen.

  • Befehl dw (delete word):
    • * innerhalb eines Wortes: aktuelles Wort löschen
    • * innerhalb von Leerzeichen: alle Leerzeichen bis zum nächsten Wort löschen
  • Befehl d$ (delete bis $) löscht alles bis zum Ende der Zeile (inkl. aktuelles Zeichen)

Etwas anders verhält es sich mit dem Löschoperator für ganze Zeilen: Um eine Zeile zu löschen, tippt man

dd

Genau wie zuvor bei den Bewegungsrichtungen gibt es auch hier die Möglichkeit, eine Wiederholungszahl anzugeben.
Um bspw. 3 Zeilen (die aktuelle und die beiden folgenden) zu löschen, wählt man:

3dd

Ersetzen

Aus dem Normalmodus heraus kann man mit dem Befehl r (Replace) ganz bequem einzelne Zeichen ersetzen.
Möchte man das aktuell markierte Zeichen durch ein q ersetzen, dann tippt man:

rq

Man kann auch größere Bereiche mit dem Befehl c (Change) ersetzen, dabei gelten die oben genannten Bewegungsrichtungen (0,$,e,b,…).
Nach dem Befehl ist man im Einfügemodus und gibt den Ersatztext ein.

Undo, Redo und Repeat

  • Mittels u (Undo) macht man den letzten Befehl rückgängig.
  • Mittels U (Undo) wird die aktuelle Zeile wiederhergestellt.
  • Der Befehl CTRL+R (Redo) wird der zuletzt mittels u zurückgenommenen Befehl wieder ausgeführt.

Dies ist nicht zu verwechseln mit einer Wiederholung des zuletzt durchgeführten Befehls: . (Punkt, Repeat).

Zwischenablage

Der Löschbefehl wirkt wie ein Ausschneiden in einem normalen Texteditor:
Die gelöschten Zeichen werden zwischengespeichert und man kann sie einfügen mittels p (Paste oder Put), dabei wird der Inhalt der Zwischenablage stets nach der aktuellen Cursorposition eingefügt, was bei ganzen Zeilen (dd,2dd,…) dazu führt, dass die neue Zeile unter der aktuellen eingefügt wird.

= Ansicht =

Zeilennummern anzeigen/verbergen

Im Befehlsmodus kann man mittels

:set number

Zeilennummern anzeigen lassen und diese mit

:set nonumber

wieder verbergen.

Die aktuelle Zeilennummer erhält man am unteren Bildschirmrand mit Ctrl+G.

Ansicht der aktuellen Datei aktualisieren (Refresh)

Aktuelle Datei neu laden:

:e

Mehrere Dateien öffnen (Tabs)

Neue Datei öffnen:

:tabe 

Zum vorherigen Tab springen:

:tabp

Zum nächsten Tab springen:

:tabn

Datei des aktuellen Tabs schließen:

:q

Alle Tabs schließen:

:qall

= Anwendungsfälle =

Man Page eines Befehls öffnen

Die zu einem Befehl gehörige Man Page kann man öffnen, indem man den Cursor im Namen des Befehls platziert und den Befehl K startet.
Verlassen kann man die Man Page wie gewohnt mit q.

Eine referenzierte Datei öffnen

Wenn man im Vim entwickelt, möchte man manchmal eine Datei öffnen, die bspw. bei C/C++ mit einem #include eingebunden wird.
Im Vim ist dies sehr einfach:
Man bewegt sich mit dem Cursor auf den Namen der referenzierten Datei und startet den Befehl (go file):

:gf

Leere Zeilen entfernen

:g/^$/d

Der Befehl :g/[regex]/[cmd] nimmt einen regulären Ausdruck [regex] (hier ^$^ ist der Zeilenanfang, $ das Zeilenende) und wendet das zuletzt stehende Kommando [cmd] (hier d) auf alle zutreffenden Zeilen an.

Suchen ohne Ersetzen

Im Befehlsmodus: Suche (case insensitive) nächstes Vorkommen von type:

/type

Nächsten Treffer anzeigen: n drücken; vorherigen Treffer anzeigen N drücken.

Case Sensitive Search

Die Suche im vim ist standardmäßig case insensitive.
Um die Suche case sensitive zu machen, muss die Sequenz \C in die Suchanfrage aufgenommen werden:

/Type\C

…findet also nur den Term Type, nicht aber type, TYpe etc.

Suchen und Ersetzen

Alle Vorkommnisse (g) von foo innerhalb einer Zeile durch bar ersetzen:

:s/foo/bar/g

Alle Vorkommnisse (g) von foo innerhalb einer Datei durch bar ersetzen:

:%s/foo/bar/g

Weitere/alternative Optionen zu g:

  • c (confirm) bestätige jede Ersetzung
  • i (ignore case) Groß-/Kleinschreibung ignorieren (d.h. im Beispiel würden auch Foo, fOo, etc. durch bar ersetzt)
  • kein g nur in der aktuellen Zeile ersetzen

Blockweise einkommentieren

Quelle: [http://notfaq.wordpress.com/2006/07/28/vim-comment-blocks-of-code/]

  • Erste Spalte des zu kommentierenden Textblocks markieren (Ctrl+v, kann mittels Ctrl+v auch wieder verlassen werden)
  • In den Einfügemodus wechseln mit I (großes i).
  • Zeilenkommentarzeichen eingeben (bspw. # bei der BASH oder // bei C)
  • ESC, um den Einfügemodus zu verlassen
  • (Manchmal erscheinen die Zeichen erst, wenn man eine weitere Taste drückt)

Blockweise auskommentieren

Haben alle Zeilen dengleichen Anfang (bspw. ###), dann kann man wie in vorigem Abschnitt vorgehen:

  • Die zu löschenden Kommentarzeichen als Textblock markieren (Ctrl+v, kann mittels Ctrl+v auch wieder verlassen werden)
  • Löschen mittels x.
  • Der Editor kehrt automatisch in den Anfangsmodus zurück.

Das Programm nmap hat den Ruf, von bösen Skriptkiddies zum Cracken von Systemen eingesetzt zu werden, was sicherlich so auch stimmen mag.
Nichtsdestoweniger kann sich nmap auch als nützliches Diagnose- und Sicherheitstool erweisen wie die folgenden Anwendungsfälle zeigen.

Alle erreichbaren Hosts zeigen

Mithilfe der Option -sP (“Ping Scan”) können alle erreichbaren Hosts im Netzwerk (hier im Adressbereich192.168.0.0 bis 192.168.0.255) gefunden werden:

$ nmap -sP 192.168.0.*

Starting Nmap 5.21 ( http://nmap.org ) at 2012-02-16 07:57 CET
Nmap scan report for easy.box (192.168.0.1)
Host is up (0.0027s latency).
Nmap scan report for 192.168.0.20
Host is up (0.00018s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.53 seconds

Offene Ports ermitteln

  • connect() Scan – Option -sT
    Brot-und-Butter-Scanmethode: nmap versucht, eine ganz reguläre TCP-Verbindung (connect Syscall) herzustellen
    Ablauf: voller Handshake: SYN -> ACK/SYN -> ACK
  • SYN Stealth Scan – Option -sS1
    Baut die Verbindung nur soweit als nötig auf, bricht dann mitten im Handshake ab (nur als root möglich!)
    Ablauf: voller Handshake: SYN -> ACK/SYN -> RST
  • FIN, Null and Xmas Tree Scans – Optionen: -sF, -sN, -sX
    Senden Pakete mit den entsprechenden Flags (also FIN, FIN,URG,PUSH, keine Flag), woraufhin eine RFC-konforme Implementierung im Fall eines offenen Ports nicht antworten und im Falle eines geschlossenen Ports ein RST senden sollte
  • Ping Scan – Option -sP
    Sendet zunächst ein reguläres Ping-Paket, falls keine Antwort eintrifft, verfährt es wie -sT (nicht root) oder -sS (root)
  • UDP Scan – Option -sU
    Sendet UDP-Pakete der Länge 0 und wartet auf die Antwort Port Unreachable für geschlossene Ports (Gefahr von False Positives, falls Firewall diese Pakete blockiert!)
  • IP Protocol Scans – Option -sO
    Sendet IP Pakete mit allen möglichen 255 IP-Protokoll-Typen und wartet auf Protocol Unreachable für nicht-laufende Protokolle (Gefahr von False Positives, falls Firewall diese Pakete blockiert!)
  • Version Detection und Fingerprinting – Optionen -sV, -O (gr. o), -A
    Ermittelt die Versionen der laufenden Services (-sV), des Betriebssystems (-O) oder von Beidem (-A)

Zusatzparameter

  • Geschwindigkeit – Option -T0 bis -T5
    Rate, mit welcher die Testpakete gesendet werden
    -T0: warte mind. 5 Minuten zwischen Paketen
    -T5: sende mit maximaler Rate
  • IPv6 aktivieren – Option -6
  • Redseligere Ausgabe (e) – Option -v
  • Portbereich einschränken – Option -p
    Portbereiche können mit Komma und Gedankenstrich getrennt werden:
    Beispiel: -p 1,2,3,1000-2000,2500 scannt die Ports 1,2,3,2500 sowie 1000-2000

Links