Index: ossp-pkg/var/Makefile RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/Makefile,v co -q -kk -p'1.1.1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/Makefile,v' | diff -u /dev/null - -L'ossp-pkg/var/Makefile' 2>/dev/null --- ossp-pkg/var/Makefile +++ - 2024-05-02 03:50:30.787444800 +0200 @@ -0,0 +1,50 @@ +# Build libvarexp. + +CC = gcc +AR = ar +RANLIB = ranlib + +WARNFLAGS = -Wall -pedantic +OPTFLAGS = -O3 -pipe + +CPPFLAGS = +CFLAGS = +LDFLAGS = + +OBJS = expand-named-characters.o expand-character-class.o command.o \ + expression.o variable.o text.o expand.o input.o tokenbuf.o \ + search-and-replace.o cut-out-offset.o transpose.o padding.o + +.c.o: + $(CC) $(CPPFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(CFLAGS) -c $< + +all: libvarexp.a + +libvarexp.a: $(OBJS) + @rm -f $@ + $(AR) cr $@ $(OBJS) + $(RANLIB) $@ + +clean:: + @(cd regression-tests && $(MAKE) clean) + rm -f $(OBJS) + rm -f libvarexp.a + +check:: + (cd regression-tests && $(MAKE) check) + +# Dependencies + +command.o: internal.h varexp.h +cut-out-offset.o: internal.h varexp.h +expand-character-class.o: internal.h varexp.h +expand-named-characters.o: internal.h varexp.h +expand.o: internal.h varexp.h +expression.o: internal.h varexp.h +input.o: internal.h varexp.h +padding.o: internal.h varexp.h +search-and-replace.o: internal.h varexp.h +text.o: internal.h varexp.h +tokenbuf.o: internal.h varexp.h +transpose.o: internal.h varexp.h +variable.o: internal.h varexp.h Index: ossp-pkg/var/Odinfile RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/Odinfile,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/Odinfile,v' | diff -u /dev/null - -L'ossp-pkg/var/Odinfile' 2>/dev/null --- ossp-pkg/var/Odinfile +++ - 2024-05-02 03:50:30.844601227 +0200 @@ -0,0 +1,23 @@ +# Build variable-expression library. + +libvarexp.a == %libvarexp.a.sm +cc='gcc -Wall -pedantic' \ + +debug \ + :a + +%libvarexp.a.sm == << + expand-named-characters.c + expand-character-class.c + command.c + expression.c + variable.c + text.c + expand.c + input.c + tokenbuf.c + search-and-replace.c + cut-out-offset.c + transpose.c + padding.c + +%clean ! == !<< + rm -f libvarexp.a Index: ossp-pkg/var/Odinfile RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/Odinfile,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/Odinfile,v' 2>/dev/null Index: ossp-pkg/var/TODO RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/TODO,v co -q -kk -p'1.1.1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/TODO,v' | diff -u /dev/null - -L'ossp-pkg/var/TODO' 2>/dev/null --- ossp-pkg/var/TODO +++ - 2024-05-02 03:50:30.851000277 +0200 @@ -0,0 +1,175 @@ + libvar.a + + - Prefix ist "var_", beziehungsweise "VAR_". + + - Eine Variable kann im Text in der Form $name oder ${name} angegeben + werden, wobei die Wahl der Klammern '{' '}' und des '$' + parametriesiert werden können. + + - Gültige Zeichen für einen Variablennamen sind konfigurierbar. + Garbage in -- garbage out. + + - Ein echtes '$'-Zeichen im Text kann durch Voranstellung eines + wählbaren Escapezeichen dargestellt werden. Default ist der + Backslash ('\'). + + - Der Aufrufer der Funktion soll steuern können, wie sich die Library + verhält, wenn eine Variable nicht existiert. Denkbar sind: + + - Abbruch mit Fehler, + - die Variable wird zu "", oder + - der Ausdruck wird unverändert in den Ausgabetext übernommen, + sodaß eventuell ein zweiter Pass gemacht werden kann. + + - ${parameter:-word} wird normal expandiert. Wenn "parameter" leer + ist, wird stattdessen "word" eingesetzt. + + - ${parameter:+word} substituiert "word" wenn "parameter" nicht leer + ist, sonst wird "" substituiert. + + - ${parameter:o-}, ${parameter:o-} + + - ${parameter:o,}, ${parameter:o,} + + - ${parameter:#} expandiert zur Länge des Inhaltes von "parameter". + + - ${parameter:s/pattern/string/[gti]} expandiert "parameter" und + führt dann eine Ersetzung mittels des regulären Ausdrucks "pattern" + durch. Wird das 'g'-Flag angegeben, wird nicht nur eine Instanz von + "pattern" durch "string" ersetzt, sondern alle. Das 't'-Flag + signalisiert, daß eine reine Text-Ersetzung ohne Unterstützung von + regulären Ausdrücken gewünscht ist. Das 'i'-Flag besagt, daß die + Suche nach "pattern" case-insensitiv durchgeführt wird. + + - ${parameter:y/ochars/nchars/} expandiert den Inhalt von "parameter" + und transformiert dabei nach dem Prinzip von tr(1) die "ochars" im + Text zu "nchars". + + - ${parameter:l} wandelt den Inhalt von "parameter" in + Kleinbuchstaben, bevor es die Variable expandiert. Dies geschieht + über toupper(3). + + - ${parameter:u} wandelt den Inhalt von "parameter" in + Großbuchstaben, bevor es die Variable expandiert. Dies geschieht + über tolower(3). + + - ${parameter:*word} expandiert zum leeren Wort, wenn "parameter" + nicht leer ist, sonst zu "word". + + - Padding: ${parameter:p///} expandiert + "parameter" in einen String der Mindestbreite , wobei abhaengig + von ("r" = right, "l" = left, "c" = center) noch fehlende + Zeichen mit aufgefuellt werden. Diest ist gedacht, um in + Templates saubere Tabellen erzeugen zu koennen. + Beispiele (foo="bar"): + "${foo:p/6/./r}" -> "bar..." + "${foo:p/6/./l}" -> "...bar" + "${foo:p/6/./c}" -> ".bar.." (oder "..bar.", egal) + "${foo:p/20/-=/c}" -> "-=-=-=-=-bar-=-=-=-=" + + - Jedes Vorkommen eines der folgenden Konstrukte im Text wird durch + das zugehörige Sonderzeichen ersetzt. + + \t tab + \n newline + \r return + \033 octal char + \x1B hex char + \x{263a} wide hex char + + - Syntax: + + input : ( TEXT | variable )* + + variable : '$' ( name | expression ) + + expression : START-DELIM ( name | variable )+ ( ':' command )* END-DELIM + + name : ( VARNAME | SPECIAL1 | SPECIAL2 )+ + + command : '-' ( EXPTEXT | variable )+ + | '+' ( EXPTEXT | variable )+ + | 'o' ( NUMBER ('-' | ',') ( NUMBER )? ) + | '#' + | '*' ( EXPTEXT | variable )+ + | 's' '/' ( variable | SUBSTTEXT )+ '/' ( variable | SUBSTTEXT )* '/' ( 'g' | 'i' | 't' )* + | 'y' '/' ( variable | SUBSTTEXT )+ '/' ( variable | SUBSTTEXT )* '/' + | 'p' '/' NUMBER '/' ( variable | SUBSTTEXT )* '/' ( 'r' | 'l' | 'c' ) + | 'l' + | 'u' + + START-DELIM : '{' + + END-DELIM : '}' + + VARNAME : '[a-zA-Z0-9_]+' + + SPECIAL1 : '[' + + SPECIAL2 : ']' + + NUMBER : '[0-9]+' + + SUBSTTEXT : '[^$/]' + + EXPTEXT : '[^$}:]+' + + TEXT : '[^$]+' + + - Doku sollte ein Beispiel für Quoting von Shell- und + Regexp-Ausdrücken enthalten. + + - Wir unterstützen PCRE-, POSIX-Regex- oder keine regulären + Ausdrücke. Dies kann über autoconf zur Compilezeit angegeben + werden. + + - Das Escaping-Problem: + +Unsere Library macht zwei Dinge: + + (1) Sie expandiert Variablen-Ausdrücke mit Unterstützung von + Operationen wie Suchen/Ersetzen, und + + (2) sie expandiert sogenannte "quoted pairs", wie zum Beispiel \n. + +Das Problem ist nun, daß sie dies in zwei Pässen tun will -- und muß. +Die Frage ist jedoch, in welcher Reihenfolge tut sie es und welche +Ergebnisse werden in den Ausgabetext ausgegeben? Betrachtet man +folgendes Beispiel, wird das Problem klarer: + + Variablen: TEST = foo + Eingabe..: Der Betrag auf Konto $TEST ist \$50. + +Soll die korrekte Ausgabe an dieser Stelle nun + + Der Betrag auf Konto foo ist \$50. + +oder + + Der Betrag auf Konto foo ist $50. + +sein? Die erste Form ist die, die man intuitiv erwartet, die zweite +Form ist jedoch die, die man braucht, wenn man den Text durch mehrere +Pässe jagen will -- was wir ausdrücklich vorgesehen haben. + +Schlimmer noch: Wie soll die Library die Eingabe + + Der Betrag auf Konto ${TEST:s/(.*)/\1bar/} ist \$50. + +interpretieren? Würde unser Parser das Token "\1" interpretieren, +würde nur eine "1" zurückbleiben, der Benutzer müßte also "\\1" +schreiben, um das Ergebnis zu erhalten, was er erwartet. +Interpretierte unser Parser die "quoted pairs" nicht, könnte man den +Ausdruck + + ${TEST:s/\n/ /g} + +nicht verwenden, weil die Regular-Expression-Funktionen ein '\n' nicht +kennen. + +Nehmen wir also an, wir interpretieren "quoted pairs" und leben damit, +daß der Benutzer dann doppelt escapen muß ... Wie verhält sich das +dann mit mehreren Pässen? Läuft die Library zweimal über die Eingabe, +bräuchte man bereits + + Der Betrag auf Konto ${TEST:s/(.*)/\\1bar/} ist \$50. Index: ossp-pkg/var/command.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/command.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/command.c,v' | diff -u /dev/null - -L'ossp-pkg/var/command.c' 2>/dev/null --- ossp-pkg/var/command.c +++ - 2024-05-02 03:50:30.853752623 +0200 @@ -0,0 +1,385 @@ +#include "internal.h" + +int command(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* data) + { + const char* p = begin; + tokenbuf tmptokbuf; + tokenbuf search, replace, flags; + tokenbuf number1, number2; + int isrange; + int rc; + + init_tokenbuf(&tmptokbuf); + init_tokenbuf(&search); + init_tokenbuf(&replace); + init_tokenbuf(&flags); + init_tokenbuf(&number1); + init_tokenbuf(&number2); + + if (begin == end) + return 0; + + switch (tolower(*p)) + { + case 'l': /* Turn data to lowercase. */ + if (data->begin) + { + char* ptr; + /* If the buffer does not life in an allocated buffer, + we have to copy it before modifying the contents. */ + + if (data->buffer_size == 0) + { + if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + } + for (ptr = (char*)data->begin; ptr != data->end; ++ptr) + *ptr = tolower(*ptr); + } + ++p; + break; + + case 'u': /* Turn data to uppercase. */ + if (data->begin) + { + char* ptr; + if (data->buffer_size == 0) + { + if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + } + for (ptr = (char*)data->begin; ptr != data->end; ++ptr) + *ptr = toupper(*ptr); + } + ++p; + break; + + case 'o': /* Cut out substrings. */ + ++p; + rc = number(p, end); + if (rc == 0) + { + rc = VAR_MISSING_START_OFFSET; + goto error_return; + } + else + { + number1.begin = p; + number1.end = p + rc; + number1.buffer_size = 0; + p += rc; + } + + if (*p == ',') + { + isrange = 0; + ++p; + } + else if (*p == '-') + { + isrange = 1; + ++p; + } + else + { + rc = VAR_INVALID_OFFSET_DELIMITER; + goto error_return; + } + + rc = number(p, end); + number2.begin = p; + number2.end = p + rc; + number2.buffer_size = 0; + p += rc; + if (data->begin) + { + rc = cut_out_offset(data, &number1, &number2, isrange); + if (rc < 0) + goto error_return; + } + break; + + case '#': /* Substitute length of the string. */ + if (data->begin) + { + char buf[1024]; + sprintf(buf, "%d", data->end - data->begin); + free_tokenbuf(data); + if (!assign_to_tokenbuf(data, buf, strlen(buf))) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + } + ++p; + break; + + case '-': /* Substitute parameter if data is empty. */ + ++p; + rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &tmptokbuf); + if (rc < 0) + goto error_return; + else if (rc == 0) + { + rc = VAR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + else + p += rc; + if (data->begin != NULL && data->begin == data->end) + { + free_tokenbuf(data); + move_tokenbuf(&tmptokbuf, data); + } + break; + + case '*': /* Return "" if data is not empty, parameter otherwise. */ + ++p; + rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &tmptokbuf); + if (rc < 0) + goto error_return; + else if (rc == 0) + { + rc = VAR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + else + p += rc; + if (data->begin != NULL) + { + if (data->begin == data->end) + { + free_tokenbuf(data); + move_tokenbuf(&tmptokbuf, data); + } + else + { + free_tokenbuf(data); + data->begin = data->end = ""; + data->buffer_size = 0; + } + } + break; + + case '+': /* Substitute parameter if data is not empty. */ + ++p; + rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &tmptokbuf); + if (rc < 0) + goto error_return; + else if (rc == 0) + { + rc = VAR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + else + p += rc; + if (data->begin != NULL) + { + if (data->begin != data->end) + { + free_tokenbuf(data); + move_tokenbuf(&tmptokbuf, data); + } + } + break; + + case 's': /* Search and replace. */ + ++p; + + if (*p != '/') + return VAR_MALFORMATTED_REPLACE; + else + ++p; + + rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &search); + if (rc < 0) + goto error_return; + else + p += rc; + + if (*p != '/') + { + rc = VAR_MALFORMATTED_REPLACE; + goto error_return; + } + else + ++p; + + rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &replace); + if (rc < 0) + goto error_return; + else + p += rc; + + if (*p != '/') + { + rc = VAR_MALFORMATTED_REPLACE; + goto error_return; + } + else + ++p; + + rc = exptext(p, end, config); + if (rc < 0) + goto error_return; + else + { + flags.begin = p; + flags.end = p + rc; + flags.buffer_size = 0; + p += rc; + } + + if (data->begin) + { + rc = search_and_replace(data, &search, &replace, &flags); + if (rc < 0) + goto error_return; + } + break; + + case 'y': /* Transpose characters from class A to class B. */ + ++p; + + if (*p != '/') + return VAR_MALFORMATTED_TRANSPOSE; + else + ++p; + + rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &search); + if (rc < 0) + goto error_return; + else + p += rc; + + if (*p != '/') + { + rc = VAR_MALFORMATTED_TRANSPOSE; + goto error_return; + } + else + ++p; + + rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &replace); + if (rc < 0) + goto error_return; + else + p += rc; + + if (*p != '/') + { + rc = VAR_MALFORMATTED_TRANSPOSE; + goto error_return; + } + else + ++p; + + if (data->begin) + { + rc = transpose(data, &search, &replace); + if (rc < 0) + goto error_return; + } + break; + + + case 'p': /* Padding. */ + ++p; + + if (*p != '/') + return VAR_MALFORMATTED_PADDING; + else + ++p; + + rc = number(p, end); + if (rc == 0) + { + rc = VAR_MISSING_PADDING_WIDTH; + goto error_return; + } + else + { + number1.begin = p; + number1.end = p + rc; + number1.buffer_size = 0; + p += rc; + } + + if (*p != '/') + { + rc = VAR_MALFORMATTED_PADDING; + goto error_return; + } + else + ++p; + + rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, + force_expand, &replace); + if (rc < 0) + goto error_return; + else + p += rc; + + if (*p != '/') + { + rc = VAR_MALFORMATTED_PADDING; + goto error_return; + } + else + ++p; + + if (*p != 'l' && *p != 'c' && *p != 'r') + { + rc = VAR_MALFORMATTED_PADDING; + goto error_return; + } + else + ++p; + + if (data->begin) + { + rc = padding(data, &number1, &replace, p[-1]); + if (rc < 0) + goto error_return; + } + break; + + default: + return VAR_UNKNOWN_COMMAND_CHAR; + } + + /* Exit gracefully. */ + + free_tokenbuf(&tmptokbuf); + free_tokenbuf(&search); + free_tokenbuf(&replace); + free_tokenbuf(&flags); + free_tokenbuf(&number1); + free_tokenbuf(&number2); + return p - begin; + + error_return: + free_tokenbuf(data); + free_tokenbuf(&tmptokbuf); + free_tokenbuf(&search); + free_tokenbuf(&replace); + free_tokenbuf(&flags); + free_tokenbuf(&number1); + free_tokenbuf(&number2); + return rc; + } Index: ossp-pkg/var/command.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/command.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/command.c,v' 2>/dev/null Index: ossp-pkg/var/cut-out-offset.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/cut-out-offset.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/cut-out-offset.c,v' | diff -u /dev/null - -L'ossp-pkg/var/cut-out-offset.c' 2>/dev/null --- ossp-pkg/var/cut-out-offset.c +++ - 2024-05-02 03:50:30.860950966 +0200 @@ -0,0 +1,58 @@ +#include "internal.h" + +size_t tokenbuf2int(tokenbuf* number) + { + const char* p; + size_t num = 0; + for (p = number->begin; p != number->end; ++p) + { + num *= 10; + num += *p - '0'; + } + return num; + } + +int cut_out_offset(tokenbuf* data, tokenbuf* number1, tokenbuf* number2, int isrange) + { + tokenbuf res; + const char* p; + size_t num1 = tokenbuf2int(number1); + size_t num2 = tokenbuf2int(number2); + + /* Determine begin of result string. */ + + if ((data->end - data->begin) < num1) + return VAR_OFFSET_OUT_OF_BOUNDS; + else + p = data->begin + num1; + + /* If num2 is zero, we copy the rest from there. */ + + if (num2 == 0) + { + if (!assign_to_tokenbuf(&res, p, data->end - p)) + return VAR_OUT_OF_MEMORY; + } + else /* OK, then use num2. */ + { + if (isrange) + { + if ((p + num2) > data->end) + return VAR_RANGE_OUT_OF_BOUNDS; + if (!assign_to_tokenbuf(&res, p, num2)) + return VAR_OUT_OF_MEMORY; + } + else + { + if (num2 < num1) + return VAR_OFFSET_LOGIC_ERROR; + if ((data->begin + num2) > data->end) + return VAR_RANGE_OUT_OF_BOUNDS; + if (!assign_to_tokenbuf(&res, p, (data->begin + num2) - p)) + return VAR_OUT_OF_MEMORY; + } + } + free_tokenbuf(data); + move_tokenbuf(&res, data); + return VAR_OK; + } Index: ossp-pkg/var/cut-out-offset.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/cut-out-offset.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/cut-out-offset.c,v' 2>/dev/null Index: ossp-pkg/var/expand-character-class.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand-character-class.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/expand-character-class.c,v' | diff -u /dev/null - -L'ossp-pkg/var/expand-character-class.c' 2>/dev/null --- ossp-pkg/var/expand-character-class.c +++ - 2024-05-02 03:50:30.867097567 +0200 @@ -0,0 +1,48 @@ +#include "internal.h" + +static void expand_range(char a, char b, char class[256]) + { + assert(a <= b); + assert(class != NULL); + + do + { + class[(int)a] = 1; + } + while (++a <= b); + } + + +var_rc_t expand_character_class(const char* desc, char class[256]) + { + size_t i; + + assert(desc != NULL); + assert(class != NULL); + + /* Clear the class array. */ + + for (i = 0; i < 256; ++i) + class[i] = 0; + + /* Walk through the class description and set the appropriate + entries in the array. */ + + while(*desc != '\0') + { + if (desc[1] == '-' && desc[2] != '\0') + { + if (desc[0] > desc[2]) + return VAR_INCORRECT_CLASS_SPEC; + expand_range(desc[0], desc[2], class); + desc += 3; + } + else + { + class[(int)*desc] = 1; + ++desc; + } + } + + return VAR_OK; + } Index: ossp-pkg/var/expand-character-class.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand-character-class.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/expand-character-class.c,v' 2>/dev/null Index: ossp-pkg/var/expand-named-characters.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand-named-characters.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/expand-named-characters.c,v' | diff -u /dev/null - -L'ossp-pkg/var/expand-named-characters.c' 2>/dev/null --- ossp-pkg/var/expand-named-characters.c +++ - 2024-05-02 03:50:30.873300717 +0200 @@ -0,0 +1,177 @@ +#include "internal.h" + +/* Internal parsing code for octal. */ + +static int isoct(char c) + { + if (c >= '0' && c <= '7') + return 1; + else + return 0; + } + +static var_rc_t expand_octal(const char** src, char** dst, const char* end) + { + unsigned char c; + + if (end - *src < 3) + return VAR_INCOMPLETE_OCTAL; + if (!isoct(**src) || !isoct((*src)[1]) || !isoct((*src)[2])) + return VAR_INVALID_OCTAL; + + c = **src - '0'; + if (c > 3) + return VAR_OCTAL_TOO_LARGE; + c *= 8; + ++(*src); + + c += **src - '0'; + c *= 8; + ++(*src); + + c += **src - '0'; + + **dst = (char)c; + ++(*dst); + return VAR_OK; + } + +static int ishex(char c) + { + if ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) + return 1; + else + return 0; + } + +/* Internal parsing code for hex. */ + +static var_rc_t expand_simple_hex(const char** src, char** dst, const char* end) + { + unsigned char c = 0; + + if (end - *src < 2) + return VAR_INCOMPLETE_HEX; + if (!ishex(**src) || !ishex((*src)[1])) + return VAR_INVALID_HEX; + + if (**src >= '0' && **src <= '9') + c = **src - '0'; + else if (c >= 'a' && c <= 'f') + c = **src - 'a' + 10; + else if (c >= 'A' && c <= 'F') + c = **src - 'A' + 10; + + c = c << 4; + ++(*src); + + if (**src >= '0' && **src <= '9') + c += **src - '0'; + else if (**src >= 'a' && **src <= 'f') + c += **src - 'a' + 10; + else if (**src >= 'A' && **src <= 'F') + c += **src - 'A' + 10; + + **dst = (char)c; + ++(*dst); + return VAR_OK; + } + +static var_rc_t expand_grouped_hex(const char** src, char** dst, const char* end) + { + var_rc_t rc; + + while (*src < end && **src != '}') + { + if ((rc = expand_simple_hex(src, dst, end)) != 0) + return rc; + ++(*src); + } + if (*src == end) + return VAR_INCOMPLETE_GROUPED_HEX; + + return VAR_OK; + } + +static var_rc_t expand_hex(const char** src, char** dst, const char* end) + { + if (*src == end) + return VAR_INCOMPLETE_HEX; + if (**src == '{') + { + ++(*src); + return expand_grouped_hex(src, dst, end); + } + else + return expand_simple_hex(src, dst, end); + } + +/* + Expand the following named characters in the buffer: + + \t tab + \n newline + \r return + \033 octal char + \x1B hex char + \x{263a} wide hex char + + Any other character quoted by a backslash is copied verbatim. +*/ + +var_rc_t expand_named_characters(const char* src, size_t len, char* dst) + { + const char* end = src + len; + var_rc_t rc; + + assert(src != NULL); + assert(dst != NULL); + + while (src < end) + { + if (*src == '\\') + { + if (++src == end) + return VAR_INCOMPLETE_NAMED_CHARACTER; + switch (*src) + { + case 'n': + *dst++ = '\n'; + break; + case 't': + *dst++ = '\t'; + break; + case 'r': + *dst++ = '\r'; + break; + case 'x': + ++src; + if ((rc = expand_hex(&src, &dst, end)) != 0) + return rc; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((rc = expand_octal(&src, &dst, end)) != 0) + return rc; + break; + default: + *dst++ = *src; + } + ++src; + } + else + *dst++ = *src++; + } + *dst = '\0'; + return VAR_OK; + } Index: ossp-pkg/var/expand-named-characters.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand-named-characters.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/expand-named-characters.c,v' 2>/dev/null Index: ossp-pkg/var/expand.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/expand.c,v' | diff -u /dev/null - -L'ossp-pkg/var/expand.c' 2>/dev/null --- ossp-pkg/var/expand.c +++ - 2024-05-02 03:50:30.879562592 +0200 @@ -0,0 +1,60 @@ +#include "internal.h" + +const var_config_t var_config_default = + { + '$', /* varinit */ + '{', /* startdelim */ + '}', /* enddelim */ + '\\', /* escape */ + "a-zA-Z0-9_" /* namechars */ + }; + +var_rc_t var_expand(const char* input_buf, size_t input_len, + char** result, size_t* result_len, + var_cb_t lookup, void* lookup_context, + const var_config_t* config, int force_expand) + { + char nameclass[256]; + var_rc_t rc; + tokenbuf output; + + /* Assert everything is as we expect it. */ + + assert(input != NULL); + assert(result != NULL); + assert(result_len != NULL); + assert(lookup != NULL); + + /* Expand the class description for valid variable names. */ + + if (config == NULL) + config = &var_config_default; + rc = expand_character_class(config->namechars, nameclass); + if (rc != VAR_OK) + return rc; + + /* Make sure that the specials defined in the configuration do not + appear in the character name class. */ + + if (nameclass[(int)config->varinit] || + nameclass[(int)config->startdelim] || + nameclass[(int)config->enddelim] || + nameclass[(int)config->escape]) + return VAR_INVALID_CONFIGURATION; + + /* Call the parser. */ + + output.begin = output.end = NULL; + output.buffer_size = 0; + rc = input(input_buf, input_buf + input_len, config, nameclass, + lookup, lookup_context, force_expand, &output); + if (rc != VAR_OK) + { + free_tokenbuf(&output); + return rc; + } + *result = (char*)output.begin; + *result_len = output.end - output.begin; + + return VAR_OK; + } Index: ossp-pkg/var/expand.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expand.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/expand.c,v' 2>/dev/null Index: ossp-pkg/var/expression.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expression.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/expression.c,v' | diff -u /dev/null - -L'ossp-pkg/var/expression.c' 2>/dev/null --- ossp-pkg/var/expression.c +++ - 2024-05-02 03:50:30.885790037 +0200 @@ -0,0 +1,163 @@ +#include "internal.h" + +int expression(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result) + { + const char* p = begin; + const char* data; + size_t len, buffer_size; + int failed = 0; + int rc; + tokenbuf name; + tokenbuf tmp; + + /* Clear the tokenbufs to make sure we have a defined state. */ + + init_tokenbuf(&name); + init_tokenbuf(&tmp); + init_tokenbuf(result); + + /* Expect STARTDELIM. */ + + if (p == end || *p != config->startdelim) + return 0; + + if (++p == end) + return VAR_INCOMPLETE_VARIABLE_SPEC; + + /* Get the name of the variable to expand. The name may consist of + an arbitrary number of VARNAMEs and VARIABLEs. */ + + do + { + rc = varname(p, end, nameclass); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + if (!append_to_tokenbuf(&name, p, rc)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + else + p += rc; + } + + rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + else + p += rc; + } + } + while (rc > 0); + + /* We must have the complete variable name now, so make sure we + do. */ + + if (name.begin == name.end) + { + rc = VAR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + + /* Now we have the name of the variable stored in "name". We + expect an ENDDELIM here. */ + + if (p == end || (*p != config->enddelim && *p != ':')) + { + rc = VAR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + else + ++p; + + /* Use the lookup callback to get the variable's contents. */ + + rc = (*lookup)(lookup_context, name.begin, name.end - name.begin, &data, &len, &buffer_size); + if (rc < 0) + goto error_return; + else if (rc == 0) + { + /* The variable is undefined. What we'll do now depends on the + force_expand flag. */ + + if (force_expand) + { + rc = VAR_UNDEFINED_VARIABLE; + goto error_return; + } + else + { + /* Initialize result to point back to the original text in + the buffer. */ + + result->begin = begin-1; + result->end = p; + result->buffer_size = 0; + failed = 1; + } + } + else + { + /* The preliminary result is the contents of the variable. + This may be modified by the commands that may follow. */ + + result->begin = data; + result->end = data + len; + result->buffer_size = buffer_size; + } + + if (p[-1] == ':') + { + /* Parse and execute commands. */ + + free_tokenbuf(&tmp); + --p; + while (p != end && *p == ':') + { + ++p; + if (!failed) + rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result); + else + rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); + if (rc < 0) + goto error_return; + p += rc; + if (failed) + result->end += rc; + } + + if (p == end || *p != config->enddelim) + { + rc = VAR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + ++p; + if (failed) + ++result->end; + } + + /* Exit gracefully. */ + + free_tokenbuf(&name); + free_tokenbuf(&tmp); + return p - begin; + + /* Exit in case of an error. */ + + error_return: + free_tokenbuf(&name); + free_tokenbuf(&tmp); + free_tokenbuf(result); + return rc; + } Index: ossp-pkg/var/expression.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/expression.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/expression.c,v' 2>/dev/null Index: ossp-pkg/var/input.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/input.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/input.c,v' | diff -u /dev/null - -L'ossp-pkg/var/input.c' 2>/dev/null --- ossp-pkg/var/input.c +++ - 2024-05-02 03:50:30.892069634 +0200 @@ -0,0 +1,54 @@ +#include "internal.h" + +var_rc_t input(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* output) + { + int rc; + tokenbuf result; + + init_tokenbuf(&result); + + do + { + rc = text(begin, end, config->varinit, config->escape); + if (rc > 0) + { + if (!append_to_tokenbuf(output, begin, rc)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + begin += rc; + } + else if (rc < 0) + goto error_return; + + rc = variable(begin, end, config, nameclass, lookup, lookup_context, force_expand, &result); + if (rc > 0) + { + if (!append_to_tokenbuf(output, result.begin, result.end - result.begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + else + begin += rc; + } + else if (rc < 0) + goto error_return; + } + while (rc > 0); + + if (begin != end) + { + rc = VAR_INPUT_ISNT_TEXT_NOR_VARIABLE; + goto error_return; + } + + return VAR_OK; + + error_return: + free_tokenbuf(&result); + return rc; + } Index: ossp-pkg/var/input.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/input.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/input.c,v' 2>/dev/null Index: ossp-pkg/var/internal.h RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/internal.h,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/internal.h,v' | diff -u /dev/null - -L'ossp-pkg/var/internal.h' 2>/dev/null --- ossp-pkg/var/internal.h +++ - 2024-05-02 03:50:30.898173493 +0200 @@ -0,0 +1,86 @@ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include +#include +#include +#include +#include +#include "varexp.h" + +#ifdef DMALLOC +# define DMALLOC_FUNC_CHECK +# include +#endif + +/* Turn character class descriptions into a lookup-array. */ + +var_rc_t expand_character_class(const char* desc, char class[256]); + +/* + The tokenbuf structure is used by the parser routines. If + buffer_size is >0, it means that the buffer has been allocated by + malloc(3) and must be free(3)ed when not used anymore. +*/ + +typedef struct + { + const char* begin; + const char* end; + size_t buffer_size; + } +tokenbuf; + +int append_to_tokenbuf(tokenbuf* output, const char* begin, size_t rc); +void free_tokenbuf(tokenbuf* buf); +void init_tokenbuf(tokenbuf* buf); +int assign_to_tokenbuf(tokenbuf* buf, const char* data, size_t len); +void move_tokenbuf(tokenbuf* src, tokenbuf* dst); + +var_rc_t input(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* output); + +int variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result); + + +int command(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result); + +int exptext(const char* begin, const char* end, const var_config_t* config); + +int exptext_or_variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result); + + +int substext_or_variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result); + +int substext(const char* begin, const char* end, const var_config_t* config); + +int search_and_replace(tokenbuf* data, tokenbuf* search, tokenbuf* replace, tokenbuf* flags); + +int varname(const char* begin, const char* end, const char nameclass[256]); + +int expression(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result); + +int text(const char* begin, const char* end, char varinit, char escape); + +int number(const char* begin, const char* end); + +int cut_out_offset(tokenbuf* data, tokenbuf* number1, tokenbuf* number2, int isrange); + +int transpose(tokenbuf* data, tokenbuf* search, tokenbuf* replace); + +int padding(tokenbuf* data, tokenbuf* width, tokenbuf* fill, char position); + +size_t tokenbuf2int(tokenbuf* number); + +#endif /* !defined(INTERNAL_H) */ Index: ossp-pkg/var/internal.h RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/internal.h,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/internal.h,v' 2>/dev/null Index: ossp-pkg/var/padding.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/padding.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/padding.c,v' | diff -u /dev/null - -L'ossp-pkg/var/padding.c' 2>/dev/null --- ossp-pkg/var/padding.c +++ - 2024-05-02 03:50:30.921469094 +0200 @@ -0,0 +1,139 @@ +#include +#include +#include "internal.h" + +int padding(tokenbuf* data, tokenbuf* widthstr, tokenbuf* fill, char position) + { + tokenbuf result; + size_t width = tokenbuf2int(widthstr); + int i; + + printf("Padding data '%s' to width '%d' by filling in '%s' to position '%c'.\n", + data->begin, width, fill->begin, position); + + if (fill->begin == fill->end) + return VAR_EMPTY_PADDING_FILL_STRING; + + init_tokenbuf(&result); + + if (position == 'l') + { + i = width - (data->end - data->begin); + if (i > 0) + { + printf("Missing %d characters at the end of the data string.\n", i); + i = i / (fill->end - fill->begin); + printf("That's %d times the padding string.\n", i); + while(i > 0) + { + if (!append_to_tokenbuf(data, fill->begin, fill->end - fill->begin)) + return VAR_OUT_OF_MEMORY; + --i; + } + i = (width - (data->end - data->begin)) % (fill->end - fill->begin); + printf("Plus a remainder of %d characters.\n", i); + if (!append_to_tokenbuf(data, fill->begin, i)) + return VAR_OUT_OF_MEMORY; + } + } + else if (position == 'r') + { + i = width - (data->end - data->begin); + if (i > 0) + { + printf("Missing %d characters at the beginning of the data string.\n", i); + i = i / (fill->end - fill->begin); + printf("That's %d times the padding string.\n", i); + while(i > 0) + { + if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + --i; + } + i = (width - (data->end - data->begin)) % (fill->end - fill->begin); + printf("Plus a remainder of %d characters.\n", i); + if (!append_to_tokenbuf(&result, fill->begin, i)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + + free_tokenbuf(data); + move_tokenbuf(&result, data); + } + } + else if (position == 'c') + { + i = (width - (data->end - data->begin)) / 2; + if (i > 0) + { + /* Create the prefix. */ + + printf("Missing %d characters at the beginning of the data string.\n", i); + i = i / (fill->end - fill->begin); + printf("That's %d times the padding string.\n", i); + while(i > 0) + { + if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + --i; + } + i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin); + printf("Plus a remainder of %d characters.\n", i); + if (!append_to_tokenbuf(&result, fill->begin, i)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + + /* Append the actual data string. */ + + if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + + /* Append the suffix. */ + + i = width - (result.end - result.begin); + printf("Missing %d characters at the end of the data string.\n", i); + i = i / (fill->end - fill->begin); + printf("That's %d times the padding string.\n", i); + while(i > 0) + { + if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + --i; + } + i = width - (result.end - result.begin); + printf("Plus a remainder of %d characters.\n", i); + if (!append_to_tokenbuf(&result, fill->begin, i)) + { + free_tokenbuf(&result); + return VAR_OUT_OF_MEMORY; + } + + /* Move string from temporary buffer to data buffer. */ + + free_tokenbuf(data); + move_tokenbuf(&result, data); + } + } + + return VAR_OK; + } Index: ossp-pkg/var/padding.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/padding.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/padding.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/.run-tests RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/.run-tests,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/.run-tests,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/.run-tests' 2>/dev/null --- ossp-pkg/var/regression-tests/.run-tests +++ - 2024-05-02 03:50:30.928448316 +0200 @@ -0,0 +1,44 @@ +#! /bin/sh + +if [ $# -lt 1 ]; then + echo "Usage: $0 test1.exe [...]" + exit 1 +fi + +RESCOLUMN=50 +numTests=0 +numFails=0 + +echo "Running test suite:" + +pad='' +n=$RESCOLUMN +while [ $n -gt 0 ]; do + pad="$pad." + n=`expr $n - 1` +done +for suite in "$@"; do + name=`basename "${suite}"` + name=`expr "${name}" : '\(.*\)\.exe$'` + echo "$name$pad" | awk '{ printf("%s ", substr($0, 0, n)); }' n=$RESCOLUMN + export DMALLOC_OPTIONS=debug=0x4f47d03,inter=1,log=/tmp/$name.dmalloc + numTests=`expr $numTests + 1` + eval ./$suite >/dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "OK" + else + numFails=`expr $numFails + 1` + echo "FAILED" + fi +done + +echo +if [ $numFails -eq 0 ]; then + echo "Summary: All tests succeeded." + exit 0 +else + percent=`expr $numFails \* 100` + percent=`expr $percent / $numTests` + echo "Summary: $numFails of $numTests tests failed ($percent%)." + exit 1 +fi Index: ossp-pkg/var/regression-tests/.run-tests RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/.run-tests,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/.run-tests,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/Makefile RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Makefile,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Makefile,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/Makefile' 2>/dev/null --- ossp-pkg/var/regression-tests/Makefile +++ - 2024-05-02 03:50:30.934585935 +0200 @@ -0,0 +1,44 @@ +# Regression tests for libvarexp. + +CC = gcc +AR = ar +RANLIB = ranlib + +WARNFLAGS = -Wall -pedantic +OPTFLAGS = -O3 -pipe + +CPPFLAGS = +CFLAGS = +LDFLAGS = + +.SUFFIXES: .exe + +TESTS = expand-named-characters.exe expand-character-class.exe \ + expand1.exe expand2.exe force-expand.exe expand3.exe \ + expand4.exe expand5.exe expand6.exe empty-search-pattern.exe \ + offset-failure.exe + +.c.exe: + $(CC) $(CPPFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< -L.. -lvarexp + +check: + @(cd .. && $(MAKE) libvarexp.a) + @$(MAKE) $(TESTS) + @./.run-tests $(TESTS) + +clean:: + rm -f $(TESTS) + +# Dependencies + +empty-search-pattern.exe: ../internal.h ../varexp.h +expand-character-class.exe: ../internal.h ../varexp.h +expand-named-characters.exe: ../internal.h ../varexp.h +expand1.exe: ../internal.h ../varexp.h +expand2.exe: ../internal.h ../varexp.h +expand3.exe: ../internal.h ../varexp.h +expand4.exe: ../internal.h ../varexp.h +expand5.exe: ../internal.h ../varexp.h +expand6.exe: ../internal.h ../varexp.h +force-expand.exe: ../internal.h ../varexp.h +offset-failure.exe: ../internal.h ../varexp.h Index: ossp-pkg/var/regression-tests/Makefile RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Makefile,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Makefile,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/Odinfile RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Odinfile,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Odinfile,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/Odinfile' 2>/dev/null --- ossp-pkg/var/regression-tests/Odinfile +++ - 2024-05-02 03:50:30.940678991 +0200 @@ -0,0 +1,81 @@ +# Regression tests for libvarexp. + +%test == () +cmd='sh' (%run-tests) \ + (expand-named-characters.exe) \ + (expand-character-class.exe) \ + (expand1.exe) \ + (expand2.exe) \ + (force-expand.exe) \ + (expand3.exe) \ + (expand4.exe) \ + (expand5.exe) \ + (expand6.exe) \ + (empty-search-pattern.exe) \ + (offset-failure.exe) \ + +output_show \ + :stdout + +expand-named-characters.exe == expand-named-characters.c +(%test.flags) :exe +expand-character-class.exe == expand-character-class.c +(%test.flags) :exe +expand1.exe == expand1.c +(%test.flags) :exe +expand2.exe == expand2.c +(%test.flags) :exe +force-expand.exe == force-expand.c +(%test.flags) :exe +expand3.exe == expand3.c +(%test.flags) :exe +expand4.exe == expand4.c +(%test.flags) :exe +expand5.exe == expand5.c +(%test.flags) :exe +expand6.exe == expand6.c +(%test.flags) :exe +empty-search-pattern.exe == empty-search-pattern.c +(%test.flags) :exe +offset-failure.exe == offset-failure.c +(%test.flags) :exe + +%test.flags == << + +lib=(../libvarexp.a) + +cc='gcc -Wall -pedantic' + +debug + +%run-tests == <<[eof] + if [ $# -lt 1 ]; then + echo "Usage: $0 test1.exe [...]" + exit 1 + fi + + RESCOLUMN=50 + numTests=0 + numFails=0 + + echo "Running test suite:" + + pad='' + n=$RESCOLUMN + while [ $n -gt 0 ]; do + pad="$pad." + n=`expr $n - 1` + done + for suite in "$@"; do + name=`basename "${suite}"` + name=`expr "${name}" : '\(.*\)\.exe$'` + echo "$name$pad" | awk '{ printf("%s ", substr($0, 0, n)); }' n=$RESCOLUMN + export DMALLOC_OPTIONS=debug=0x4f47d03,inter=1,log=/tmp/$name.dmalloc + numTests=`expr $numTests + 1` + eval $suite >/dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "OK" + else + numFails=`expr $numFails + 1` + echo "FAILED" + fi + done + + echo + if [ $numFails -eq 0 ]; then + echo "Summary: All tests succeeded." + exit 0 + else + percent=`expr $numFails \* 100` + percent=`expr $percent / $numTests` + echo "Summary: $numFails of $numTests tests failed ($percent%)." + exit 1 + fi +[eof] + +%clean ! == !<< + rm -f *.exe Index: ossp-pkg/var/regression-tests/Odinfile RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Odinfile,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/Odinfile,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/empty-search-pattern.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/empty-search-pattern.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/empty-search-pattern.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/empty-search-pattern.c' 2>/dev/null --- ossp-pkg/var/regression-tests/empty-search-pattern.c +++ - 2024-05-02 03:50:30.946910392 +0200 @@ -0,0 +1,48 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +int main(int argc, char** argv) + { + const char* input = "${HOME:s/$EMPTY/test/}"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + if (setenv("HOME", "/home/regression-tests", 1) != 0 || + setenv("EMPTY", "", 1) != 0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &env_lookup, NULL, + NULL, 0); + if (rc != VAR_EMPTY_SEARCH_STRING) + { + printf("var_expand() should have failed with VAR_EMPTY_SEARCH_STRING but returned %d.\n", rc); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/empty-search-pattern.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/empty-search-pattern.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/empty-search-pattern.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand-character-class.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-character-class.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-character-class.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand-character-class.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand-character-class.c +++ - 2024-05-02 03:50:30.953050156 +0200 @@ -0,0 +1,56 @@ +#include "../internal.h" + +static void class2string(char class[256], char* buf) + { + size_t i; + for (i = 0; i < 256; ++i) + { + if (class[i]) + *buf++ = (char)i; + } + *buf = '\0'; + } + +struct test_case + { + const char* input; + const char* expected; + var_rc_t rc; + }; + +int main(int argc, char** argv) + { + struct test_case tests[] = + { + { "", "", VAR_OK }, + { "abcabc", "abc", VAR_OK }, + { "a-z", "abcdefghijklmnopqrstuvwxyz", VAR_OK }, + { "a-eA-Eabcdef-", "-ABCDEabcdef", VAR_OK }, + { "-a-eA-Eabcdef-", "-ABCDEabcdef", VAR_OK }, + { "0-9-", "-0123456789", VAR_OK }, + { "g-a", NULL, VAR_INCORRECT_CLASS_SPEC } + }; + size_t i; + char class[256]; + char tmp[1024]; + + for (i = 0; i < sizeof(tests) / sizeof(struct test_case); ++i) + { + if (expand_character_class(tests[i].input, class) != tests[i].rc) + { + printf("expand_character_class() failed test case %d.\n", i); + return 1; + } + if (tests[i].expected != NULL) + { + class2string(class, tmp); + if (strcmp(tmp, tests[i].expected) != 0) + { + printf("expand_character_class() failed test case %d.\n", i); + return 1; + } + } + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand-character-class.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-character-class.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-character-class.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand-named-characters.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-named-characters.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-named-characters.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand-named-characters.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand-named-characters.c +++ - 2024-05-02 03:50:30.959195662 +0200 @@ -0,0 +1,49 @@ +#include "../internal.h" + +struct test_case + { + const char* input; + const char* expected; + var_rc_t rc; + }; + +int main(int argc, char** argv) + { + struct test_case tests[] = + { + { "", "", VAR_OK }, + { "\\", NULL, VAR_INCOMPLETE_NAMED_CHARACTER }, + { "hello world", "hello world", VAR_OK }, + { "\\n", "\n", VAR_OK }, + {"\\t", "\t", VAR_OK }, + { "\\rhello\\tworld\\n", "\rhello\tworld\n", VAR_OK }, + { "\\x5a\\x5A", "ZZ", VAR_OK }, + { "\\x5g\\x5A", NULL, VAR_INVALID_HEX }, + { "\\x5", NULL, VAR_INCOMPLETE_HEX }, + { "\\033", "\033", VAR_OK }, + { "\\03", NULL, VAR_INCOMPLETE_OCTAL }, + { "\\038", NULL, VAR_INVALID_OCTAL }, + { "\\400", NULL, VAR_OCTAL_TOO_LARGE }, + { "\\x{4243}", "BC", VAR_OK }, + { "\\x{}", "", VAR_OK }, + { "\\x{5a5A5a5A}", "ZZZZ", VAR_OK }, + { "\\x{", NULL, VAR_INCOMPLETE_GROUPED_HEX }, + { "x\\x{5a5A5a5A}a", "xZZZZa", VAR_OK }, + { "x\\x{5a5A5a\\0015A}a", NULL, VAR_INVALID_HEX }, + { "x\\x{5a\\x{5a}5A}a", NULL, VAR_INVALID_HEX } + }; + size_t i; + char tmp[1024]; + + for (i = 0; i < sizeof(tests) / sizeof(struct test_case); ++i) + { + if (expand_named_characters(tests[i].input, strlen(tests[i].input), tmp) != tests[i].rc || + (tests[i].expected != NULL && strcmp(tmp, tests[i].expected) != 0)) + { + printf("expand_named_characters() failed test case %d.\n", i); + return 1; + } + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand-named-characters.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-named-characters.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand-named-characters.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand1.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand1.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand1.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand1.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand1.c +++ - 2024-05-02 03:50:30.965391239 +0200 @@ -0,0 +1,45 @@ +#include "../internal.h" + +int dummy(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + printf("The dummy callback should not have been called!\n"); + exit(1); + } + +int main(int argc, char** argv) + { + const char* input = "This is a \\$test!"; + const char* output = "This is a \\$test!"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &dummy, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + if (tmp_len != strlen(output)) + { + printf("The length of the output string is not what we expected: %d != %d.\n", + tmp_len, strlen(output)); + return 1; + } + + if (memcmp(tmp, output, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + free(tmp); + + return 0; + } Index: ossp-pkg/var/regression-tests/expand1.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand1.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand1.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand2.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand2.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand2.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand2.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand2.c +++ - 2024-05-02 03:50:30.971527259 +0200 @@ -0,0 +1,56 @@ +#include "../internal.h" + +int dummy(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + if (name_len != sizeof("test")-1) + { + printf("The the length of the variable name (%d) doesn't fit.\n", name_len); + exit(1); + } + if (memcmp(varname, "test", sizeof("test")-1) != 0) + { + printf("Callback called for unknown variable.\n"); + exit(1); + } + *data = "foobar"; + *data_len = sizeof("foobar")-1; + *buffer_size = 0; + + return 1; + } + +int main(int argc, char** argv) + { + const char* input = "This is a $test!"; + const char* output = "This is a foobar!"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &dummy, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + if (tmp_len != strlen(output)) + { + printf("The length of the output string is not what we expected: %d != %d.\n", + tmp_len, strlen(output)); + return 1; + } + + if (memcmp(tmp, output, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand2.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand2.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand2.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand3.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand3.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand3.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand3.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand3.c +++ - 2024-05-02 03:50:30.977666578 +0200 @@ -0,0 +1,81 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +int main(int argc, char** argv) + { + const char* input = \ + "\\$HOME = '$HOME'\\n" \ + "\\$OSTYPE = '$OSTYPE'\\n" \ + "\\$UNDEFINED = '$UNDEFINED'\\n" \ + "\\$TERM = '$TERM'\\n"; + const char* output = \ + "$HOME = '/home/regression-tests'\n" \ + "$OSTYPE = 'regression-os'\n" \ + "$UNDEFINED = '$UNDEFINED'\n" \ + "$TERM = 'regression-term'\n"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + if (setenv("HOME", "/home/regression-tests", 1) != 0 || + setenv("OSTYPE", "regression-os", 1) != 0 || + setenv("TERM", "regression-term", 1) != 0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + unsetenv("UNDEFINED"); + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &env_lookup, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + rc = expand_named_characters(tmp, tmp_len, tmp); + if (rc != VAR_OK) + { + printf("expand_named_characters() failed with error %d.\n", rc); + return 1; + } + else + tmp_len = strlen(tmp); + + if (tmp_len != strlen(output)) + { + printf("The length of the output string is not what we expected: %d != %d.\n", + tmp_len, strlen(output)); + return 1; + } + + if (memcmp(tmp, output, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand3.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand3.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand3.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand4.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand4.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand4.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand4.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand4.c +++ - 2024-05-02 03:50:30.983877966 +0200 @@ -0,0 +1,81 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +int main(int argc, char** argv) + { + const char* input = \ + "\\$HOME = '${HOME}'\\n" \ + "\\$OSTYPE = '${OSTYPE}'\\n" \ + "\\$UNDEFINED = '${UNDEFINED}'\\n" \ + "\\$TERM = '${TERM}'\\n"; + const char* output = \ + "$HOME = '/home/regression-tests'\n" \ + "$OSTYPE = 'regression-os'\n" \ + "$UNDEFINED = '${UNDEFINED}'\n" \ + "$TERM = 'regression-term'\n"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + if (setenv("HOME", "/home/regression-tests", 1) != 0 || + setenv("OSTYPE", "regression-os", 1) != 0 || + setenv("TERM", "regression-term", 1) != 0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + unsetenv("UNDEFINED"); + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &env_lookup, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + rc = expand_named_characters(tmp, tmp_len, tmp); + if (rc != VAR_OK) + { + printf("expand_named_characters() failed with error %d.\n", rc); + return 1; + } + else + tmp_len = strlen(tmp); + + if (tmp_len != strlen(output)) + { + printf("The length of the output string is not what we expected: %d != %d.\n", + tmp_len, strlen(output)); + return 1; + } + + if (memcmp(tmp, output, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand4.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand4.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand4.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand5.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand5.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand5.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand5.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand5.c +++ - 2024-05-02 03:50:30.990128632 +0200 @@ -0,0 +1,85 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +int main(int argc, char** argv) + { + const char* input = \ + "\\$HOME = '${HOME}'\\n" \ + "\\$OSTYPE = '${$FOO${BAR}}'\\n" \ + "\\$UNDEFINED = '${UNDEFINED}'\\n" \ + "\\$TERM = '${TERM}'\\n"; + const char* output = \ + "$HOME = '/home/regression-tests'\n" \ + "$OSTYPE = 'regression-os'\n" \ + "$UNDEFINED = '${UNDEFINED}'\n" \ + "$TERM = 'regression-term'\n"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + if (setenv("HOME", "/home/regression-tests", 1) != 0 || + setenv("OSTYPE", "regression-os", 1) != 0 || + setenv("TERM", "regression-term", 1) != 0 || + setenv("FOO", "OS", 1) != 0 || + setenv("BAR", "TYPE", 1) != 0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + unsetenv("UNDEFINED"); + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &env_lookup, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + rc = expand_named_characters(tmp, tmp_len, tmp); + if (rc != VAR_OK) + { + printf("expand_named_characters() failed with error %d.\n", rc); + return 1; + } + else + tmp_len = strlen(tmp); + + printf("==================================================\n%s==================================================\n", tmp); + + if (tmp_len != strlen(output)) + { + printf("The length of the output string is not what we expected: %d != %d.\n", + tmp_len, strlen(output)); + return 1; + } + + if (memcmp(tmp, output, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand5.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand5.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand5.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/expand6.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand6.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand6.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/expand6.c' 2>/dev/null --- ossp-pkg/var/regression-tests/expand6.c +++ - 2024-05-02 03:50:30.996343526 +0200 @@ -0,0 +1,121 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +struct test_case + { + const char* input; + const char* expected; + }; + +int main(int argc, char** argv) + { + const struct test_case tests[] = + { + { "$HOME", "/home/regression-tests" }, + { "${FOO}", "os" }, + { "${BAR}", "type" }, + { "${${FOO:u}${BAR:u}:l:u}", "REGRESSION-OS" }, + { "${UNDEFINED}", "${UNDEFINED}" }, + { "${OSTYPE:#}", "13" }, + { "${EMPTY:-test${FOO}test}", "testostest" }, + { "${EMPTY:-test${FOO:u}test}", "testOStest" }, + { "${TERM:-test${FOO}test}", "regression-term" }, + { "${EMPTY:+FOO}", "" }, + { "${HOME:+test${FOO}test}", "testostest" }, + { "${HOME:+${OS${BAR:u}}}", "regression-os" }, + { "${HOME:+OS${UNDEFINED:u}}", "OS${UNDEFINED:u}" }, + { "${UNDEFINED:+OS${BAR:u}}", "${UNDEFINED:+OS${BAR:u}}" }, + { "${HOME:*heinz}", "" }, + { "${EMPTY:*claus}", "claus" }, + { "${TERM}", "regression-term" }, + { "${HOME:s/reg/bla/}", "/home/blaression-tests" }, + { "${HOME:s/e/bla/}", "/hombla/regression-tests" }, + { "${HOME:s/e/bla/g}", "/hombla/rblagrblassion-tblasts" }, + { "${HOME:s/\\//_/g}", "_home_regression-tests" }, + { "${HOME:s/[eso]/_/g}", "/h_m_/r_gr___i_n-t__t_" }, + { "${HOME:s/[esO]/_/g}", "/hom_/r_gr___ion-t__t_" }, + { "${HOME:s/[esO]/_/gi}", "/h_m_/r_gr___i_n-t__t_" }, + { "${OSTYPE:s/^[re]/_/g}", "_egression-os" }, + { "${EMPTY:s/^[re]/_/g}", "" }, + { "${HOME:s/.*/heinz/}", "heinz" }, + { "${HOME:s/e/bla/t}", "/hombla/regression-tests" }, + { "${HOME:s/E/bla/t}", "/home/regression-tests" }, + { "${HOME:s/E/bla/ti}", "/hombla/regression-tests" }, + { "${HOME:s/E/bla/tig}", "/hombla/rblagrblassion-tblasts" }, + { "${HOME:o1-5}", "home/" }, + { "${HOME:o1,5}", "home" }, + { "${HOME:o5,}", "/regression-tests" }, + { "${HOME:o5-}", "/regression-tests" }, + { "${HOME:o7,13}", "egress" }, + { "${HOME:y/a-z/A-YZ/}", "/HOME/REGRESSION-TESTS" }, + { "${HOME:y/e-g/a-c/}", "/homa/racrassion-tasts" }, + { "${FOO:p/15/../l}", "os............." }, + { "${FOO:p/15/12345/l}", "os1234512345123" }, + { "${FOO:p/15/../r}", ".............os" }, + { "${FOO:p/15/12345/r}", "1234512345123os" }, + { "${FOO:p/15/../c}", "......os......." }, + { "${FOO:p/15/12345/c}", "123451os1234512" } + }; + /* + { "${HOME:s/g(res)s/x\\\\1x/g}","/homE/rEgrEssion-tEsts" } + { "${HOME:s/\\x65/\\x45/g}", "/home/regression-tests" } + */ + char* tmp; + size_t tmp_len; + var_rc_t rc; + size_t i; + + if (setenv("HOME", "/home/regression-tests", 1) != 0 || + setenv("OSTYPE", "regression-os", 1) != 0 || + setenv("TERM", "regression-term", 1) != 0 || + setenv("FOO", "os", 1) != 0 || + setenv("BAR", "type", 1) != 0 || + setenv("EMPTY", "", 1) != 0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + unsetenv("UNDEFINED"); + + for (i = 0; i < sizeof(tests) / sizeof(struct test_case); ++i) + { + rc = var_expand(tests[i].input, strlen(tests[i].input), + &tmp, &tmp_len, + &env_lookup, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("Test case #%d: var_expand() failed with return code %d.\n", i, rc); + return 1; + } + if (tmp_len != strlen(tests[i].expected) || tmp == NULL || memcmp(tests[i].expected, tmp, tmp_len) != 0) + { + printf("Test case #%d: Expected result '%s' but got '%s'.\n", i, tests[i].expected, tmp); + return 1; + } + printf("Test case #%02d: '%s' --> '%s'.\n", i, tests[i].input, tmp); + free(tmp); + } + + return 0; + } Index: ossp-pkg/var/regression-tests/expand6.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand6.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/expand6.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/force-expand.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/force-expand.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/force-expand.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/force-expand.c' 2>/dev/null --- ossp-pkg/var/regression-tests/force-expand.c +++ - 2024-05-02 03:50:31.002649205 +0200 @@ -0,0 +1,65 @@ +#include "../internal.h" + +int dummy(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + if (name_len != sizeof("heinz_ist_doof")-1) + { + printf("The the length of the variable name (%d) doesn't fit.\n", name_len); + exit(1); + } + if (memcmp(varname, "heinz_ist_doof", sizeof("heinz_ist_doof")-1) != 0) + { + printf("Callback called for unknown variable.\n"); + exit(1); + } + return 0; /* say it's undefined */ + } + +int main(int argc, char** argv) + { + const char* input = "This is a $heinz_ist_doof!"; + char* tmp; + size_t tmp_len; + var_rc_t rc; + + /* Run var_expand() with force_expand and expect failure. */ + + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &dummy, NULL, + NULL, 1); + if (rc != VAR_UNDEFINED_VARIABLE) + { + printf("var_expand() should have failed with error UNDEFINED_VARIABLE, but returned %d.\n", rc); + return 1; + } + + /* Run var_expand() without force_expand and expect input == output. */ + + rc = var_expand(input, strlen(input), + &tmp, &tmp_len, + &dummy, NULL, + NULL, 0); + if (rc != VAR_OK) + { + printf("var_expand() failed with error %d.\n", rc); + return 1; + } + + if (tmp_len != strlen(input)) + { + printf("The length of the input string is not what we expected: %d != %d.\n", + tmp_len, strlen(input)); + return 1; + } + + if (memcmp(tmp, input, tmp_len) != 0) + { + printf("The buffer returned by var_expand() is not what we expected.\n"); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/force-expand.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/force-expand.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/force-expand.c,v' 2>/dev/null Index: ossp-pkg/var/regression-tests/offset-failure.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/offset-failure.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/offset-failure.c,v' | diff -u /dev/null - -L'ossp-pkg/var/regression-tests/offset-failure.c' 2>/dev/null --- ossp-pkg/var/regression-tests/offset-failure.c +++ - 2024-05-02 03:50:31.008806807 +0200 @@ -0,0 +1,68 @@ +#include "../internal.h" + +int env_lookup(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size) + { + char tmp[256]; + + if (name_len > sizeof(tmp)-1) + { + printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1)); + exit(1); + } + memcpy(tmp, varname, name_len); + tmp[name_len] = '\0'; + *data = getenv(tmp); + if (*data == NULL) + return 0; + *data_len = strlen(*data); + *buffer_size = 0; + return 1; + } + +int main(int argc, char** argv) + { + const char* input1 = "${HOME:o88,}"; + const char* input2 = "${HOME:o88-90}"; + const char* input3 = "${HOME:o8-90}"; + const char* input4 = "${HOME:o8,90}"; + const char* input5 = "${HOME:o8,4}"; + + char* tmp; + size_t tmp_len; + var_rc_t rc; + + if (setenv("HOME", "/home/regression-tests", 1) !=0) + { + printf("Failed to set the environment: %s.\n", strerror(errno)); + return 1; + } + if ((rc = var_expand(input1, strlen(input1), &tmp, &tmp_len, &env_lookup, NULL, NULL, 0)) != VAR_OFFSET_OUT_OF_BOUNDS) + { + printf("var_expand() should have failed with VAR_OFFSET_OUT_OF_BOUNDS but returned %d.\n", rc); + return 1; + } + if ((rc = var_expand(input2, strlen(input2), &tmp, &tmp_len, &env_lookup, NULL, NULL, 0)) != VAR_OFFSET_OUT_OF_BOUNDS) + { + printf("var_expand() should have failed with VAR_OFFSET_OUT_OF_BOUNDS but returned %d.\n", rc); + return 1; + } + if ((rc = var_expand(input3, strlen(input3), &tmp, &tmp_len, &env_lookup, NULL, NULL, 0)) != VAR_RANGE_OUT_OF_BOUNDS) + { + printf("var_expand() should have failed with VAR_RANGE_OUT_OF_BOUNDS but returned %d.\n", rc); + return 1; + } + if ((rc = var_expand(input4, strlen(input4), &tmp, &tmp_len, &env_lookup, NULL, NULL, 0)) != VAR_RANGE_OUT_OF_BOUNDS) + { + printf("var_expand() should have failed with VAR_RANGE_OUT_OF_BOUNDS but returned %d.\n", rc); + return 1; + } + if ((rc = var_expand(input5, strlen(input5), &tmp, &tmp_len, &env_lookup, NULL, NULL, 0)) != VAR_OFFSET_LOGIC_ERROR) + { + printf("var_expand() should have failed with VAR_OFFSET_LOGIC_ERROR but returned %d.\n", rc); + return 1; + } + + return 0; + } Index: ossp-pkg/var/regression-tests/offset-failure.c RCS File: /v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/offset-failure.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/regression-tests/Attic/offset-failure.c,v' 2>/dev/null Index: ossp-pkg/var/search-and-replace.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/search-and-replace.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/search-and-replace.c,v' | diff -u /dev/null - -L'ossp-pkg/var/search-and-replace.c' 2>/dev/null --- ossp-pkg/var/search-and-replace.c +++ - 2024-05-02 03:50:31.015032682 +0200 @@ -0,0 +1,158 @@ +#include +#include +#include "internal.h" + +int search_and_replace(tokenbuf* data, tokenbuf* search, tokenbuf* replace, tokenbuf* flags) + { + const char* p; + int case_insensitive = 0; + int global = 0; + int no_regex = 0; + int rc; + + if (search->begin == search->end) + return VAR_EMPTY_SEARCH_STRING; + + printf("Search '%s' in '%s' and replace it with '%s'.\n", + search->begin, data->begin, replace->begin); + + for (p = flags->begin; p != flags->end; ++p) + { + switch (tolower(*p)) + { + case 'i': + case_insensitive = 1; + printf("case_insensitive = 1;\n"); + break; + case 'g': + global = 1; + printf("global = 1;\n"); + break; + case 't': + no_regex = 1; + printf("no_regex = 1;\n"); + break; + default: + return VAR_UNKNOWN_REPLACE_FLAG; + } + } + + if (no_regex) + { + tokenbuf tmp; + init_tokenbuf(&tmp); + + for (p = data->begin; p != data->end; ) + { + if (case_insensitive) + rc = strncasecmp(p, search->begin, search->end - search->begin); + else + rc = strncmp(p, search->begin, search->end - search->begin); + if (rc != 0) + { /* no match, copy character */ + if (!append_to_tokenbuf(&tmp, p, 1)) + { + free_tokenbuf(&tmp); + return VAR_OUT_OF_MEMORY; + } + ++p; + } + else + { + append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin); + p += search->end - search->begin; + if (!global) + { + if (!append_to_tokenbuf(&tmp, p, data->end - p)) + { + free_tokenbuf(&tmp); + return VAR_OUT_OF_MEMORY; + } + break; + } + } + } + + free_tokenbuf(data); + move_tokenbuf(&tmp, data); + } + else + { + tokenbuf tmp; + tokenbuf mydata; + regex_t preg; + regmatch_t pmatch[33]; + int regexec_flag; + + /* Copy the pattern and the data to our own buffer to make + sure they're terminated with a null byte. */ + + if (!assign_to_tokenbuf(&tmp, search->begin, search->end - search->begin)) + return VAR_OUT_OF_MEMORY; + if (!assign_to_tokenbuf(&mydata, data->begin, data->end - data->begin)) + { + free_tokenbuf(&tmp); + return VAR_OUT_OF_MEMORY; + } + + /* Compile the pattern. */ + + printf("data is.................: '%s'\n", mydata.begin); + printf("regex search pattern is.: '%s'\n", tmp.begin); + printf("regex replace pattern is: '%s'\n", replace->begin); + rc = regcomp(&preg, tmp.begin, REG_EXTENDED | ((case_insensitive) ? REG_ICASE : 0)); + free_tokenbuf(&tmp); + if (rc != 0) + { + free_tokenbuf(&mydata); + return VAR_INVALID_REGEX_IN_REPLACE; + } + printf("Subexpression in pattern: '%d'\n", preg.re_nsub); + + /* Match the pattern and create the result string in the tmp + buffer. */ + + for (p = mydata.begin; p != mydata.end; ) + { + if (p == mydata.begin || p[-1] == '\n') + regexec_flag = 0; + else + regexec_flag = REG_NOTBOL; + if (regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH) + { + printf("No match; appending remainder ('%s') to output string.\n", p); + append_to_tokenbuf(&tmp, p, mydata.end - p); + break; + } + else + { +#if 0 + printf("Match from offset %ld to %ld in string '%s'.\n", + pmatch[0].rm_so, pmatch[0].rm_eo, p); +#endif + if (!append_to_tokenbuf(&tmp, p, pmatch[0].rm_so) || + !append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin)) + { + regfree(&preg); + free_tokenbuf(&tmp); + free_tokenbuf(&mydata); + return VAR_OUT_OF_MEMORY; + } + else + p += pmatch[0].rm_eo; + if (!global) + { + append_to_tokenbuf(&tmp, p, mydata.end - p); + break; + } + } + } + + regfree(&preg); + free_tokenbuf(data); + move_tokenbuf(&tmp, data); + free_tokenbuf(&mydata); + } + + return VAR_OK; + } Index: ossp-pkg/var/search-and-replace.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/search-and-replace.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/search-and-replace.c,v' 2>/dev/null Index: ossp-pkg/var/text.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/text.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/text.c,v' | diff -u /dev/null - -L'ossp-pkg/var/text.c' 2>/dev/null --- ossp-pkg/var/text.c +++ - 2024-05-02 03:50:31.021338642 +0200 @@ -0,0 +1,173 @@ +#include "internal.h" + +int text(const char* begin, const char* end, char varinit, char escape) + { + const char* p; + for (p = begin; p != end && *p != varinit; ++p) + { + if (*p == escape) + { + if (p+1 == end) + return VAR_INCOMPLETE_QUOTED_PAIR; + else + ++p; + } + } + return p - begin; + } + +int varname(const char* begin, const char* end, const char nameclass[256]) + { + const char* p; + for (p = begin; p != end && nameclass[(int)*p]; ++p) + ; + return p - begin; + } + +int number(const char* begin, const char* end) + { + const char* p; + for (p = begin; p != end && isdigit(*p); ++p) + ; + return p - begin; + } + +int substext(const char* begin, const char* end, const var_config_t* config) + { + const char* p; + for (p = begin; p != end && *p != config->varinit && *p != '/'; ++p) + { + if (*p == config->escape) + { + if (p+1 == end) + return VAR_INCOMPLETE_QUOTED_PAIR; + else + ++p; + } + } + return p - begin; + } + +int exptext(const char* begin, const char* end, const var_config_t* config) + { + const char* p; + for (p = begin; p != end && *p != config->varinit && *p != config->enddelim && *p != ':'; ++p) + { + if (*p == config->escape) + { + if (p+1 == end) + return VAR_INCOMPLETE_QUOTED_PAIR; + else + ++p; + } + } + return p - begin; + } + +int exptext_or_variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result) + { + const char* p = begin; + tokenbuf tmp; + int rc; + + init_tokenbuf(result); + init_tokenbuf(&tmp); + + if (begin == end) + return 0; + + do + { + rc = exptext(p, end, config); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + if (!append_to_tokenbuf(result, p, rc)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + else + p += rc; + } + + rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + p += rc; + if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + } + } + while (rc > 0); + + free_tokenbuf(&tmp); + return p - begin; + + error_return: + free_tokenbuf(&tmp); + free_tokenbuf(result); + return rc; + } + +int substext_or_variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result) + { + const char* p = begin; + tokenbuf tmp; + int rc; + + init_tokenbuf(result); + init_tokenbuf(&tmp); + + if (begin == end) + return 0; + + do + { + rc = substext(p, end, config); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + if (!append_to_tokenbuf(result, p, rc)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + else + p += rc; + } + + rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); + if (rc < 0) + goto error_return; + else if (rc > 0) + { + p += rc; + if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + } + } + while (rc > 0); + + free_tokenbuf(&tmp); + return p - begin; + + error_return: + free_tokenbuf(&tmp); + free_tokenbuf(result); + return rc; + } Index: ossp-pkg/var/text.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/text.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/text.c,v' 2>/dev/null Index: ossp-pkg/var/tokenbuf.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/tokenbuf.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/tokenbuf.c,v' | diff -u /dev/null - -L'ossp-pkg/var/tokenbuf.c' 2>/dev/null --- ossp-pkg/var/tokenbuf.c +++ - 2024-05-02 03:50:31.027595224 +0200 @@ -0,0 +1,115 @@ +#include "internal.h" + +#define VAR_INITIAL_BUFFER_SIZE 1 + +void init_tokenbuf(tokenbuf* buf) + { + buf->begin = buf->end = NULL; + buf->buffer_size = 0; + } + +void move_tokenbuf(tokenbuf* src, tokenbuf* dst) + { + dst->begin = src->begin; + dst->end = src->end; + dst->buffer_size = src->buffer_size; + init_tokenbuf(src); + } + +int assign_to_tokenbuf(tokenbuf* buf, const char* data, size_t len) + { + char* p = malloc(len+1); + if (p) + { + memcpy(p, data, len); + buf->begin = p; + buf->end = p + len; + buf->buffer_size = len + 1; + *((char*)(buf->end)) = '\0'; + return 1; + } + else + return 0; + } + +int append_to_tokenbuf(tokenbuf* output, const char* data, size_t len) + { + char* new_buffer; + size_t new_size; + + /* Is the tokenbuffer initialized at all? If not, allocate a + standard-sized buffer to begin with. */ + + if (output->begin == NULL) + { + if ((output->begin = output->end = malloc(VAR_INITIAL_BUFFER_SIZE)) == NULL) + return 0; + else + output->buffer_size = VAR_INITIAL_BUFFER_SIZE; + } + + /* Does the token contain text, but no buffer has been allocated + yet? */ + + if (output->buffer_size == 0) + { + /* Check whether data borders to output. If, we can append + simly by increasing the end pointer. */ + + if (output->end == data) + { + output->end += len; + return 1; + } + + /* OK, so copy the contents of output into an allocated buffer + so that we can append that way. */ + + else + { + char* tmp = malloc(output->end - output->begin + len + 1); + if (!tmp) + return 0; + memcpy(tmp, output->begin, output->end - output->begin); + output->buffer_size = output->end - output->begin; + output->begin = tmp; + output->end = tmp + output->buffer_size; + output->buffer_size += len + 1; + } + } + + /* Does the token fit into the current buffer? If not, realloc a + larger buffer that fits. */ + + if ((output->buffer_size - (output->end - output->begin)) <= len) + { + new_size = output->buffer_size; + do + { + new_size *= 2; + } + while ((new_size - (output->end - output->begin)) <= len); + new_buffer = realloc((char*)output->begin, new_size); + if (new_buffer == NULL) + return 0; + output->end = new_buffer + (output->end - output->begin); + output->begin = new_buffer; + output->buffer_size = new_size; + } + + /* Append the data at the end of the current buffer. */ + + memcpy((char*)output->end, data, len); + output->end += len; + *((char*)output->end) = '\0'; + return 1; + } + + +void free_tokenbuf(tokenbuf* buf) + { + if (buf->begin != NULL && buf->buffer_size > 0) + free((char*)buf->begin); + buf->begin = buf->end = NULL; + buf->buffer_size = 0; + } Index: ossp-pkg/var/tokenbuf.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/tokenbuf.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/tokenbuf.c,v' 2>/dev/null Index: ossp-pkg/var/transpose.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/transpose.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/transpose.c,v' | diff -u /dev/null - -L'ossp-pkg/var/transpose.c' 2>/dev/null --- ossp-pkg/var/transpose.c +++ - 2024-05-02 03:50:31.033882981 +0200 @@ -0,0 +1,97 @@ +#include +#include +#include "internal.h" + +static int expand_class_description(tokenbuf* src, tokenbuf* dst) + { + unsigned char c, d; + const char* p = src->begin; + while(p != src->end) + { + if ((src->end - p) >= 3 && p[1] == '-') + { + printf("Expand class.\n"); + if (*p > p[2]) + return VAR_INCORRECT_TRANSPOSE_CLASS_SPEC; + for (c = *p, d = p[2]; c <= d; ++c) + { + if (!append_to_tokenbuf(dst, (char*)&c, 1)) + return VAR_OUT_OF_MEMORY; + } + p += 3; + } + else + { + printf("Copy verbatim.\n"); + if (!append_to_tokenbuf(dst, p, 1)) + return VAR_OUT_OF_MEMORY; + else + ++p; + } + } + return VAR_OK; + } + +int transpose(tokenbuf* data, tokenbuf* search, tokenbuf* replace) + { + tokenbuf srcclass, dstclass; + const char* p; + int rc; + size_t i; + + init_tokenbuf(&srcclass); + init_tokenbuf(&dstclass); + + if ((rc = expand_class_description(search, &srcclass)) != VAR_OK) + goto error_return; + if ((rc = expand_class_description(replace, &dstclass)) != VAR_OK) + goto error_return; + + printf("Transpose from '%s' to '%s'.\n", + srcclass.begin, dstclass.begin); + + if (srcclass.begin == srcclass.end) + { + rc = VAR_EMPTY_TRANSPOSE_CLASS; + goto error_return; + } + if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) + { + rc = VAR_TRANSPOSE_CLASSES_MISMATCH; + goto error_return; + } + + if (data->buffer_size == 0) + { + tokenbuf tmp; + if (!assign_to_tokenbuf(&tmp, data->begin, data->end - data->begin)) + { + rc = VAR_OUT_OF_MEMORY; + goto error_return; + } + move_tokenbuf(&tmp, data); + } + + for (p = data->begin; p != data->end; ++p) + { + for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) + { + if (*p == srcclass.begin[i]) + { + *((char*)p) = dstclass.begin[i]; + break; + } + } + } + + free_tokenbuf(&srcclass); + free_tokenbuf(&dstclass); + return VAR_OK; + + error_return: + free_tokenbuf(search); + free_tokenbuf(replace); + free_tokenbuf(&srcclass); + free_tokenbuf(&dstclass); + return rc; + } Index: ossp-pkg/var/transpose.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/transpose.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/transpose.c,v' 2>/dev/null Index: ossp-pkg/var/varexp.h RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/varexp.h,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/varexp.h,v' | diff -u /dev/null - -L'ossp-pkg/var/varexp.h' 2>/dev/null --- ossp-pkg/var/varexp.h +++ - 2024-05-02 03:50:31.040105918 +0200 @@ -0,0 +1,127 @@ +#ifndef LIB_VARIABLE_EXPAND_H +#define LIB_VARIABLE_EXPAND_H + +#include + +/* Error codes returned by the varexp library. */ + +typedef enum + { + VAR_EMPTY_PADDING_FILL_STRING = -32, + VAR_MISSING_PADDING_WIDTH = -31, + VAR_MALFORMATTED_PADDING = -30, + VAR_INCORRECT_TRANSPOSE_CLASS_SPEC = -29, + VAR_EMPTY_TRANSPOSE_CLASS = -28, + VAR_TRANSPOSE_CLASSES_MISMATCH = -27, + VAR_MALFORMATTED_TRANSPOSE = -26, + VAR_OFFSET_LOGIC_ERROR = -25, + VAR_OFFSET_OUT_OF_BOUNDS = -24, + VAR_RANGE_OUT_OF_BOUNDS = -23, + VAR_INVALID_OFFSET_DELIMITER = -22, + VAR_MISSING_START_OFFSET = -21, + VAR_EMPTY_SEARCH_STRING = -20, + VAR_MISSING_PARAMETER_IN_COMMAND = -19, + VAR_INVALID_REGEX_IN_REPLACE = -18, + VAR_UNKNOWN_REPLACE_FLAG = -17, + VAR_MALFORMATTED_REPLACE = -16, + VAR_UNKNOWN_COMMAND_CHAR = -14, + VAR_INPUT_ISNT_TEXT_NOR_VARIABLE = -13, + VAR_UNDEFINED_VARIABLE = -12, + VAR_INCOMPLETE_VARIABLE_SPEC = -11, + VAR_OUT_OF_MEMORY = -10, + VAR_INVALID_CONFIGURATION = -9, + VAR_INCORRECT_CLASS_SPEC = -8, + VAR_INCOMPLETE_GROUPED_HEX = -7, + VAR_INCOMPLETE_OCTAL = -6, + VAR_INVALID_OCTAL = -5, + VAR_OCTAL_TOO_LARGE = -4, + VAR_INVALID_HEX = -3, + VAR_INCOMPLETE_HEX = -2, + VAR_INCOMPLETE_NAMED_CHARACTER = -1, + VAR_INCOMPLETE_QUOTED_PAIR = -1, + VAR_OK = 0 + } +var_rc_t; + +/* + Expand the following named characters to their binary + representation: + + \t tab + \n newline + \r return + \033 octal char + \x1B hex char + \x{263a} wide hex char + + Any other character quoted by a backslash is copied verbatim. +*/ + +var_rc_t expand_named_characters(const char* src, size_t len, char* dst); + +/* + The callback will be called by variable_expand(), providing the + following parameterns: + + context - passed through from variable_expand()'s + parameters + varname - pointer to the name of the variable to + expand + name_len - length of the string starting at varname + data - location, where the callback should store + the pointer to the contents of the looked-up + variable + data_len - location, where the callback should store + the length of the data + malloced_buffer - location, where the callback should store + either TRUE or FALSE, telling the framework + whether the buffer must be free(3)ed. + + The return code is interpreted as follows: + >0 - OK + 0 - undefined variable + <0 - error +*/ + +typedef int (*var_cb_t)(void* context, + const char* varname, size_t name_len, + const char** data, size_t* data_len, size_t* buffer_size); + +/* + This structure configures the parser's specials. I think, the fields + are pretty self-explanatory. The only one worth mentioning is + force_expand, which is a boolean. If set to TRUE, variable_expand() + will fail with an error if the lookup callback returns "undefined + variable". If set to FALSE, variable_expand() will copy the + expression that failed verbatimly to the output so that another pass + may expand it. + + The comments after each field show the default configuration. +*/ + +typedef struct + { + char varinit; /* '$' */ + char startdelim; /* '{' */ + char enddelim; /* '}' */ + char escape; /* '\' */ + char* namechars; /* 'a-zA-Z0-9_' */ + } +var_config_t; +extern const var_config_t var_config_default; + +/* + variable_expand() will parse the contents of input for variable + expressions and expand them using the provided lookup callback. The + pointer to the resulting buffer is stored in result, its length in + result_len. The buffer is always terminated by a '\0' byte, which is + not included in the result_len count. The buffer must be free(3)ed + by the caller. +*/ + +var_rc_t var_expand(const char* input, size_t input_len, + char** result, size_t* result_len, + var_cb_t lookup, void* lookup_context, + const var_config_t* config, int force_expand); + +#endif /* !defined(LIB_VARIABLE_EXPAND_H) */ Index: ossp-pkg/var/varexp.h RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/varexp.h,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/varexp.h,v' 2>/dev/null Index: ossp-pkg/var/variable.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/variable.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/Attic/variable.c,v' | diff -u /dev/null - -L'ossp-pkg/var/variable.c' 2>/dev/null --- ossp-pkg/var/variable.c +++ - 2024-05-02 03:50:31.046452703 +0200 @@ -0,0 +1,63 @@ +#include "internal.h" + +int variable(const char* begin, const char* end, const var_config_t* config, + const char nameclass[256], var_cb_t lookup, void* lookup_context, + int force_expand, tokenbuf* result) + { + const char* p = begin; + const char* data; + size_t len, buffer_size; + int rc, rc2; + + /* Clear the result tokenbuf to make sure we're in a defined + state. */ + + init_tokenbuf(result); + + /* Expect VARINIT. */ + + if (p == end || *p != config->varinit) + return 0; + + if (++p == end) + return VAR_INCOMPLETE_VARIABLE_SPEC; + + /* Try to read the variable name. If that fails, we're parsing a + complex expression. */ + + rc = varname(p, end, nameclass); + if (rc < 0) + return rc; + else if (rc > 0) + { + rc2 = (*lookup)(lookup_context, p, rc, &data, &len, &buffer_size); + if (rc2 < 0) + return rc2; + else if (rc2 == 0) + { + if (force_expand) + return VAR_UNDEFINED_VARIABLE; + else + { + result->begin = begin; + result->end = begin + 1 + rc; + result->buffer_size = 0; + return 1 + rc; + } + } + else + { + result->begin = data; + result->end = data + len; + result->buffer_size = buffer_size; + return 1 + rc; + } + } + + /* OK, we're dealing with a complex expression here. */ + + rc = expression(p, end, config, nameclass, lookup, lookup_context, force_expand, result); + if (rc > 0) + ++rc; + return rc; + } Index: ossp-pkg/var/variable.c RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/variable.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/var/Attic/variable.c,v' 2>/dev/null