Wer eine plattformunabhängige Lösung für den Zugriff auf Extended Attributes benötigt, für den habe ich hier eine kleine "Bibliothek", bestehend aus einer .c und einer .h Datei. Lizenz ist 0BSD, daher kann jeder den Code verwenden wie er will.
Aktuell werden Linux, FreeBSD, Solaris und macOS unterstützt. Eine Implementierung, die unter Windows Alternate Data Streams verwendet, kommt vermutlich noch.
Im Gegensatz zu C++ oder vielen anderen Sprachen unterstützt C keine Default Values für Funktionsparameter. Auch ein Sprachkonstrukt wie Named Parameters, welches es z.B. in Python gibt, kennt C nicht.
Häufig wird als Workaround dafür Variadic Parameters verwendet, z.B. laut Linux-Manpage gibt es den open-Syscall in zwei Varianten:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
In Wahrheit sieht die Signatur allerdings so aus:
int open(const char *pathname, int flags, ...);
Das funktioniert grundsätzlich, hat jedoch den Nachteil, dass für alle zusätzlichen optionalen Parameter keine Typüberprüfung stattfindet.
Es gibt jedoch eine Alternative die sich Compound Literale für structs bemächtigt. Die Idee ist, dass alle Parameter an die Funktion per struct übergeben werden. Damit es wie ein normaler Funktionsaufruf aussieht, wird das ganze als Makro hübsch verpackt, welches die Makro-Parameter zu einem Compound Literal macht.
#include <stdio.h>
typedef struct {
int a;
int b;
int c;
} TestFuncArgs;
#define testfunc(...) testfunc_f((TestFuncArgs){ __VA_ARGS__})
void testfunc_f(TestFuncArgs args) {
printf("a: %d b: %d c: %d\n", args.a, args.b, args.c);
}
int main(int argc, char** argv) {
testfunc(1, 2, 3);
testfunc(4, 5);
testfunc(.a = 10, .c = 20);
//testfunc("hello"); //warning
return 0;
}
Hier findet für alle Parameter eine Typüberprüfung statt und zusätzlich gibt es auch die Option, Designated Initializers zu verwenden, daher anstatt die struct-Werte als einfache Liste in richtiger Reihenfolge anzugeben, kann auch einfach der Feldname angegeben werden.
Finde den Syntax-Fehler in folgendem C-Code:
#include <stdio.h>
int main(int argc, char **argv) {
int a = 3;
switch(a) {
case 1:
printf("1\n");
break;
case 2:
printf("2\n");
int c2 = 0;
break;
case 3:
int c3 = 3;
printf("%d\n", c3);
break;
}
return 0;
}
Ein ähnlicher Fall wie hier im Beispiel hatte sich bei mir zugetragen. Der Compiler bemängelte die Variablendeklaration nach dem Case. Das heißt, hier im Beispiel ist die fehlerhafte Zeile die Variablendeklaration im Case 3.
Ich gebe zu, dass mich dies erstmal verwirrt hat und ich dachte, ich hätte versehentlich im C90-Modus kompiliert. Der Grund, wieso dies einen Fehler wirft, ist, dass ein Case nichts anderes als ein Label ist und Variablendeklarationen in C nicht mit einem Label versehen werden können.
Aus der beliebten Serie WTF C:
bool trigraphs_are_available() {
// Are trigraphs available??/
return false;
return true;
}
Was passiert hier? Zwei return-Statements hintereinander ergeben doch keinen Sinn! Kann die Funktion jemals true zurückgeben?
Auch dieses mal handelt es sich um standardkonformes C und es gibt auch Fälle, in denen die Funktion true liefert. Der Trick ist hier, dass im Kommentar etwas versteckt ist, nämlich am Ende ein Trigraph. Es ist nämlich in C möglich, manche Sonderzeichen durch alternative Zeichenketten auszudrücken. Am Ende des Kommentars steht der Trigraph ??/
, der zu einem Backslash aufgelöst wird und ein Backslash am Ende eines einzeiligen Kommentars erweitert diesen auf die nächste Zeile. Wenn also der Compiler Trigraphen unterstützt, sind die ersten beiden Zeilen der Funktion ein Kommentar und übrig bleibt nur return true
.
Gefunden habe ich das Beispiel hier.
Die Entwicklung von dav 1.3 ist ganz und gar nicht so verlaufen wie ich mir das vorgestellt hatte. Über ein Jahr später als geplant, konnte ich das Release dann vor kurzem endlich fertig stellen. Ich entwickel das jetzt allerdings im meiner Freizeit, daher variiert es natürlich, wie viel Zeit ich in die Entwicklung stecke. Trotzdem muss ich zugeben, dass die Zeit, die ich investiert habe, nicht unbedingt optimal genutzt wurde.
Schauen wir uns zunächst ein paar Zahlen der Entwicklung an.
version date loc new-loc n-month lines/month
---------------------------------------------------------------
begin 2012-11-30 0 lines 0 0 0
1.0.0 2017-08-13 14633 lines 14633 57 256
1.1.0 2017-10-07 15416 lines 783 2 391
1.2.0 2018-06-26 20465 lines 5049 8 631
1.3.0 2019-12-15 29743 lines 9278 18 515
Für die erste Version von dav habe ich natürlich relativ lange gebraucht. Es ist vermutlich deutlich einfacher, an eine bestehende Software, die man auch noch gut kennt, neues Zeug ranzubasteln, als etwas komplett neues aus dem Boden zu stampfen.
Bei der Entwicklung von dav 1.2 war ich am produktivsten. Aber wenn man nur nach lines/month geht, dann war dav 1.3 eigentlich kein Desaster (im Vergleich zu den anderen Releases). Was man aber sieht ist, dass sich die Code-Basis drastisch vergrößert hat. Viel zu viele neue Features haben den Weg in das Release gefunden. Genau das ist eines der Probleme.
Das größte Ärgernis dabei war, dass ich zwar relativ zügig neue Features implementiert hatte, aber sobald die minimal irgendwie funktioniert haben, bin ich zum nächsten neuen Feature übergegangen. Am Ende hatte ich haufen Neues, das aber nicht gut funktionierte.
Dazu kam auch ein größeres Refactoring der dav-sync Codebasis. Dieses hat natürlich bereits bestehende Funktionalität, die fehlerfrei lief, auch etwas zerstört.
Am Ende war ich Monatelang damit beschäftig, massenweise Tests zu schreiben, um überhaupt wieder Vertrauen in den Code zu entwickeln. Dies war jedoch recht erfolgreich, die Grundfunktionalität dürfte stabiler als vorher sein.
Was ich hoffentlich daraus gelernt habe: Dinge zuende entwickeln. Keine halben Sachen liegen lassen. Und am besten schließt man die Entwicklung eines neuen Features ab, in dem man diverse Tests dafür geschrieben hat, die alle problemlos durchlaufen.
Auch sollte man sich nicht zu viel auf einmal vornehmen. Ich hoffe ich kann den Spruch Release early, release often in Zukunft mehr anwenden. Das hat durchaus seine Vorteile.
Kommentare
Andreas | Artikel: Datenanalyse in der Shell Teil 1: Basis-Tools
Einfach und cool!
Danke Andreas
Rudi | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Peter | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Damit wird Nedit durch XNedit ersetzt.
Danke!
Olaf | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Anti-Aliasing hängt von der Schriftart ab. Mit einem bitmap font sollte die Schrift klassisch wie in nedit aussehen.
Einfach unter Preferences -> Default Settings -> Text Fonts nach einer passenden Schriftart suchen.
Peter | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Mettigel | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Ich hatte gedacht, dass der GX-415 im s920 deutlich mehr Dampf hat als der Raspi4.
Mein Thinclient verbraucht mit 16 GB RAM ~11 W idle, das ist das Dreifache vom RP4. Das muss man dem kleinen echt lassen... Sparsam ist er.
Olaf | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Ergebnisse von der Ultra 80 wären natürlich interessant, insbesondere im Vergleich mit dem rpi1.
kosta | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
ich hätt hier zugriff auf Ultra-80 4CPU 4GB 2x Elite3D.