• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

C In-Place String Replace mit Memmove -> Speicherfehler

electric.larry

\''; DROP TABLE user; --
Teammitglied

Registriert
13 Dez. 2014
Beiträge
4.549
Ort
Raum 43
Insofern das taglist.tags[tagList.tagCount] ein gültiger reservierter Speicherbereich ist und ich richtig verstehe, was du da machen willst, dann sollte das hier funktionieren:

[src=c]tagList.tags[tagList.tagCount].name = (char*) malloc(strlen(currentTag) );[/src]
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #22
Eigentlich habe ich den Speicherbereich für den Tag hier vorbereitet:

[src=c]tagList.tags = (tag*) malloc(sizeof (tag))[/src]

und weiter unten folgt nochmal ein, beim Hinzufügen:
tagList.tagCount = 0

[src=c]tagList.tags = (tag*) realloc(tagList.tags, sizeof (tag) * (tagList.tagCount + 1));[/src]
 

electric.larry

\''; DROP TABLE user; --
Teammitglied

Registriert
13 Dez. 2014
Beiträge
4.549
Ort
Raum 43
Ich glaub dein Denkfehler ist der:

strlen(pointer auf den string/char array) gibt dir die Länge der Zeichenkette. Also bei "Electric.Larry" z. B. 14.
sizeof(pointer auf den string/char array) gibt dir die größe der Variable, nicht der Zeichenkette. Also z. B. 8 bei einem char* Pointer auf einem 64-Bit System. Auf einem 32-Bit Rechner würdest du z. B. 4 bekommen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #24
Ne das kann eigentlich nicht sein... du hast aber natürlich Recht.

"tag" <-- ist ein Struct, kein String, deßhalb das "sizeof( tag )" für den malloc/realloc.



Hier mal der ganze Code bisher mit Makefile... soll ein XML to Json Converter werden der allerdings Zeichenweise aus einer Datei einließt... (falls jemand sich da durchblicken kann/möchte*)

Anhang anzeigen starlight.zip

XML Sample file (sample.xml) genannt, wird automatisch geladen aus dem gleichen Verzeichnis:

Die Daten mit denen ich teste sind hier:
https://msdn.microsoft.com/en-us/library/ms762271(v=vs.85).aspx

* 300 zeilen, aber viel luft drin, das meist sind kommentare und switch statements.
 
Zuletzt bearbeitet:

exomo

NGBler

Registriert
1 Aug. 2015
Beiträge
129
"invalid old size" kenne ich eigentlich nur von realloc. Bei malloc habe ich das noch nie gesehen. Den ganzen Code anzuschauen fehlt mir im Moment auch die Zeit, vielleicht schaue ich am Wochenende mal rein.

Mir ist der folgende Ausschnitt noch ins Auge gefallen:

tagList.tags[tagList.tagCount] ...

Wenn tagCount tatsächlich die Anzahl der Elemente in tags angibt, dann wäre der höchste zulässige Element im Array tagList.tags[tagList.tagCount-1]. Mit tagList.tags[tagList.tagCount] greifst du außerhalb deines reservierten Speichers zu. Da du aber mit "sizeof (tag) * (tagList.tagCount + 1)" als größe den Speicher anforderst müsste das eigentlich passen, ist aber trotzdem ein merkwürdiges vorgehen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #27
@exomo:

Danke für das Feedback, aber nur um deine Vermutung aufzuklären. tagCount ist "0" (Null) bei dem Aufruf, daher beim realloc ein sizeof(tag) * (tagCount + 1) (der counter wird erst später erhöht nachdem das Element angelegt ist.

Ansonsten wäre es natürlich klasse wenn du den Code überfliegen könntest.

@Electric.Larry
Es ist nicht eilig, es wäre aber auch in deinem Fall klasse wenn du drüberschauen könntest :T
 

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Hi.

Ich vermute stark, die Fehlermeldung ist irreführend. Zu dem Zeitpunkt, wo die auftritt, bist du nämlich schon tief im undefined behaviour. [Btw, an die C-Experten: Gibt’s das formal in der C-Spec oder ist das was C++-eigenes?]

Das Problem geht bei Zl. 146 los. Dort forderst du Speicher für tagList an, initialisierst ihn aber niemals. Bei tagList.tags ist das unkritisch, weil du den Pointer malloc’st, bevor du ihn verwendest. Anders bei tagList.tagCount. Da steht bei der ersten Verwendung irgendein Wert drin, weil malloc im Gegensatz zu calloc den zugewiesenen Speicher nicht nullt. Wenn da 0 steht, ist das reiner Zufall.

Beim realloc Zl. 218 und in der For-Schleife Zl. 263 benutzt du tagCount so, als stünde dort die Anzahl der in tagList.tags vorhandenen Elemente. Macht bei dem Namen auch absolut Sinn. Dann liest du aber in Zl. 222 übers Ende deines Arrays hinaus. Wegen dem 0-basierten Index ist das letzte Element tagcount-1. Spätestens jetzt bist du bis über beide Ohren undefined und dein Programm kann beliebigen Mist machen.

Bis auf die tagCount-Fehler sieht es erstmal gut aus. Gewundert hat mich nur, dass du in Zl. 222 beim Namen für genau ein Zeichen Speicher anforderst. Soll das so?


Nochwas anderes: Du schreibst einen Mischmasch aus C und C++. Das geht gut, weil du zum Compilieren g++ verwendest, also den C++-Compiler. Wirf mal einen C-Compiler (gcc) auf deinen Code, der haut ihn dir schnurstracks um die Ohren. Zum C Lernen tust du dir so keinen Gefallen.

Oh, und
# -w suppresses all warnings
COMPILER_FLAGS = -w
Die solltest du an lassen! Wenn der Compiler warnt, ist praktisch immer wirklich was faul. Möglicherweise hätten die die Warnungen schon sehr konkrete Hinweise auf die Probleme von oben gegeben.
 

exomo

NGBler

Registriert
1 Aug. 2015
Beiträge
129
So weit wie Brother John war ich noch gar nicht gekommen :D

1. Ich habe auch festgestellt dass ein C Compiler das nicht ohne weiteres compiliert. Wenn du C programmieren willst dann nimm auch einen C Compiler.
2. Globale Variablen sind in der Regel böse. Um Fehler zu vermeiden sollten Variablen immer so lokal wie möglich definiert werden. Bei C ist das mindestens die Funktion in der sie benutzt werden. (Neueres C kann glaube ich auch Deklarationen mitten im Code). Gerade sowas wie Zählervariablen i,j,k haben global nichts verloren.
3. Allgemeiner Tipp: Strukturiere deinen Code in kleinere Funktionen, so kann man das fast nicht lesen.

Und nun zu den Speicherfehlern:
4.1 char* arg = (char*) malloc(sizeof (char) * 2);
Zwar nicht direkt ein Fehler (außer was bei 4.2 steht), aber wenn es sich vermeiden lässt vermeide dynamische Speicheranforderungen am besten ganz. Ein statisches Array ist außreichend wenn du vorher weißt wie groß es sein soll. : char arg[2];
4.2 String zu kurz:
strncpy(arg, argv, 2);
argv += 2;

value = (char*) realloc(value, sizeof (char) * strlen(argv));
strcpy(value, argv);

Da ein string immer 0-terminiert sein sollte, musst du für einen string mit Länge N mindestens N+1 Elemente im Array haben. Im Falle von strncpy solltest du außerdem die 0-terminierunf noch explizit setzen.

5. Realloc auf nicht ge-malloc-tem Speicher:
sourceFilePath = (char*) realloc(sourceFilePath, sizeof (char) * strlen(value));
outputFilePath = (char*) realloc(outputFilePath, sizeof (char) * strlen(value));

sourceFilePath zeigt auf ein Stringliteral. Stringliterale liegen irgendwo im Datenbereich des Programms, und nicht auf dem Heap. Darauf ein realloc aufzurufen ist nicht sinnvoll. Gemerkt hättest du das, wenn du sourceFilePath als Array deklariert hättest: char sourceFilePath[] = "sample.xml"; Damit lässt der Compiler nicht zu dass du dem Array einen neuen Wert zuweist. Und natürlich gilt wieder 4.2.

6. String-Zuweisung:
outputFilePath = "output.json";

Wenn outputFilePath als Array deklariert wäre würde das wieder nicht funktionieren. Strings solltest du immer mit strcpy zuweisen. Außerdem setzt du den String hier nur wieder auf den Wert den er Ursprünglich hatte. Das riecht nach komischer Logik.

7. Unnötige c/m/re-alloc
char* tagData = (char*) calloc(1, sizeof (char));
char* tagKey = (char*) calloc(1, sizeof (char));
char* tagValue = (char*) calloc(1, sizeof (char));

char* currentTag = (char*) malloc(sizeof (char));
char* previousTag = (char*) malloc(sizeof (char));

Das sieht mir nach sehr unnötigen allocs aus. Und vor allem in der Programmlogik nicht nachvollziehbar. Mir ist schon klar dass du das machst um hinterher realloc darauf anwenden zu können, aber jedes Zeichen einzeln einzulesen und dafür einen realloc zu machen ist Gift für die Performance. Versuche lieber im Voraus genug speicher zu Reservieren und verwende z.B. ein statisches Array. Speicher ist auf heutigen Systemen genug vorhanden, dass man nicht jedes einzelne Byte wegoptimieren muss. Das gleiche gilt zwar auch für Laufzeit, aber gerade Speicherallokierungen sind sehr teuer und sollten nicht zu oft gemacht werden.

8. Das was Brother John sagt. Und vermutlich noch mehr, die ganzen string operationen durchschaue ich jetzt nicht mehr. Wenn du deinen Code aufgeräumt hast und immer noch Probleme hast schaue ich es mit vielleicht noch genauer an. Der Code darf gerne doppelt so lang sein, wenn er dafür die Operationen vernünftig in Funktionen aufteilt.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #30
Jetzt funktioniert es alles einwandfrei.

Ich hab die Postings von euch durchgearbeitet so gut es ging. Und den Code aufgeräumt.

Also, das Makefile zum Code verwendet nun gcc und -std=c99 zum kompilieren.
Damit bin ich erstmal die ganzen Compiler Fehler durchgegangen.

Danach die Tips von Brother John, wobei er Recht hatte das der Wert "tagCount" nicht initialisiert war und "irgendwas" hätte sein können, aber nicht 0. Gleich behoben.
Das letzte Element ist aber "tagCount" und nicht "tagCount -1" im Code - der Counter wird erst erhöht nachdem das Element angelegt ist. ;)

Der Fehler mit dem 1 Zeichen für "currentTag" ist nun auch beboben so das "strlen(currentTag)+1" Zeichen allokiert werden (auch der Hinweis von exomo).

@exomo,

ich gehe an dieser Stelle nicht auf jeden Punkt ein, aber wie beschrieben ist die "strlen(string)+1" und das letzte Zeichen eines Char Arrays jetzt Nullterminiert, auch nach strcpy-Anweisungen (was echt umständlich ist...)

Globale Zählervariablen sind entfernt und werden nun direkt in den Schleifen (daher die Option -std=c99) initialisiert.

sourceFilePath und outputFilePath sind nun nicht mehr hardcoded, dies war aber lazyness von meiner Seite beim testen um die Parameterübergabe zu vermeiden, ist nun aber auch behoben. Also Parameter sind erforderlich.

Die Speicherallokierungen habe ich noch drin, aber es wäre jetzt wohl ein leichtes eine fixen Speicherbereich für jetzt Element mit calloc vorzureservieren.


Und last but not least, der aktuelle Stand - tut nicht viel, aber die gesamte Logik und Schaltungen müssen nochmal überarbeitetet werden damit XML Tags korrekt geparst werden können, Key-Values usw...

Also "non working" but "political correct" ;)

Danke euch allen! :)

[src=c]/// Import c header files
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Enum and tructs

enum dataTypes {
ROOT = 0,
NUMBER = 1,
TEXT = 2,
ARRAY = 3
};

struct keyValue {
char* key;
char* value;
};

struct tag {
bool isDataTag;
unsigned int dataLength;
char* name;
char* tagData;
struct keyValue* data;
};

struct taglist {
unsigned int tagCount;
struct tag* tags;
};

// Function declarations
void cleanupApplication();

// Variables
char* sourceFilePath = NULL;
char* outputFilePath = NULL;


char* jsonData;
FILE* sourceFile;
FILE* outputFile;
struct taglist tagList;

// Main

int main(int argc, char* argv[]) {
char* arg = (char*) malloc(sizeof (char) * 3);
char* value = NULL;
atexit(cleanupApplication);

/* Comment me out for debugging*/
//sourceFilePath = (char*) calloc(11, sizeof (char));
//strcpy(sourceFilePath, "sample.xml");
//sourceFilePath[10] = '\0';

// Parse the commands given
for (int i = 1; i < argc; i++) {
if (strlen(argv) > 2) {
strncpy(arg, argv, 2);
arg[3] = '\0';
argv += 2;

if (value == NULL) {
value = (char*) calloc(strlen(argv) + 1, sizeof (char));
} else {
value = (char*) realloc(value, sizeof (char) * (strlen(argv) + 1));
}
strcpy(value, argv);
value[ strlen(argv) ] = '\0';

switch (arg[1]) {
case 'i':
sourceFilePath = (char*) malloc(sizeof (char) * (strlen(value) + 1));
strcpy(sourceFilePath, value);
printf("Source: %s\n", value);
break;
case 'o':
outputFilePath = (char*) malloc(sizeof (char) * (strlen(value) + 1));
strcpy(outputFilePath, value);
printf("Output: %s\n", value);
default:
break;
}
}
}

if (arg != NULL) {
free(arg);
}

if (value != NULL) {
free(value);
}


// Reading the source file
if (sourceFilePath == NULL) {
printf("No input file specified...\n");
exit(0);
} else {
if (outputFilePath == NULL) {
outputFilePath = (char*) realloc(outputFilePath, sizeof (char)*(strlen("output.json") + 1));
strcpy(outputFilePath, "output.json");
outputFilePath[strlen("output.json")] = '\0';
}

unsigned int bufferLength = 0;

// Get and create a buffer for the source data
sourceFile = fopen(sourceFilePath, "r");
if (sourceFile == NULL) {
printf("Error opening input file.\n");
exit(1);
} else {
fseek(sourceFile, 0, SEEK_END);
bufferLength = ftell(sourceFile);
rewind(sourceFile);

char* srcData = (char*) calloc(bufferLength, sizeof (char));
char* tagData = (char*) calloc(1, sizeof (char));
char* tagKey = (char*) calloc(1, sizeof (char));
char* tagValue = (char*) calloc(1, sizeof (char));

char* currentTag = (char*) calloc(1, sizeof (char));
char* previousTag = (char*) calloc(1, sizeof (char));

tagData[0] = '\0';
tagKey[0] = '\0';
tagValue[0] = '\0';

currentTag[0] = '\0';
previousTag[0] = '\0';

bool hasOpenTag = false;
bool isInsideTag = false;
bool tagHasData = false;
bool insideKeyValue = false;
bool ignoreTag = false;
bool hasPreviousSpace = false;
bool specialCharAhead = false;

tagList.tagCount = 0;
tagList.tags = NULL;
tagList.tags = (struct tag*) malloc(sizeof (struct tag));

unsigned int tagWritePos = 0, dataWritePos = 0;
char data = '\0';

while (!feof(sourceFile)) {
data = fgetc(sourceFile);

switch (data) {
case '?':
if (hasOpenTag && tagWritePos == 0) {
hasOpenTag = false;
ignoreTag = true;
continue;
}
break;
case '\\':
specialCharAhead = true;
break;
case '\n':
case '\r':
continue;
break;
case '/':
if (hasOpenTag) {
continue;
}
case '<':
if (!specialCharAhead && !hasOpenTag) {
hasOpenTag = true;
isInsideTag = false;
tagWritePos = 0;
specialCharAhead = false;
continue;
}

break;
case '>':
if (ignoreTag) {
ignoreTag = false;
hasOpenTag = false;
tagWritePos = 0;
currentTag = (char*) realloc(currentTag, sizeof (char));
currentTag[0] = '\0';

tagData = (char*) realloc(tagData, sizeof (char));
tagData[0] = '\0';
dataWritePos = 0;

isInsideTag = true;
continue;
}

// React if we are insideKeyValuePair
if (insideKeyValue) {
printf("key: %s | value: %s\n", tagKey, tagValue);
insideKeyValue = false;
tagWritePos = 0;


tagValue = (char*) realloc(tagValue, sizeof (char));
tagValue[0] = '\0';
tagKey = (char*) realloc(tagKey, sizeof (char));
tagKey[0] = '\0';
}

tagHasData = false;

if (!specialCharAhead && hasOpenTag) {
if (strlen(tagData) > 1) {
if (tagList.tags[tagList.tagCount - 1].tagData == NULL) {
tagList.tags[tagList.tagCount - 1].isDataTag = true;
tagList.tags[tagList.tagCount - 1].tagData = (char*) calloc(strlen(tagData) + 1, sizeof (char));
strcpy(tagList.tags[tagList.tagCount - 1].tagData, tagData);
tagList.tags[tagList.tagCount - 1].tagData[strlen(tagData)] = '\0';
}
}

tagData = (char*) realloc(tagData, sizeof (char));
tagData[0] = '\0';
dataWritePos = 0;

if (strcmp(currentTag, previousTag) != 0) {
tagList.tags = (struct tag*) realloc(tagList.tags, sizeof (struct tag) * (tagList.tagCount + 1));
tagList.tags[tagList.tagCount].name = (char*) malloc(sizeof (char) * (strlen(currentTag) + 1));
strcpy(tagList.tags[tagList.tagCount].name, currentTag);
tagList.tags[tagList.tagCount].name[strlen(currentTag)] = '\0';
tagList.tagCount++;
}

previousTag = (char*) realloc(previousTag, sizeof (char) * (strlen(currentTag) + 1));
strcpy(previousTag, currentTag);
previousTag[strlen(currentTag)] = '\0';

hasOpenTag = false;
tagWritePos = 0;

currentTag = (char*) realloc(currentTag, sizeof (char));
currentTag[0] = '\0';
isInsideTag = true;
continue;
} else {
specialCharAhead = false;
}

break;
case ' ':
if (isInsideTag) {
if (!hasPreviousSpace) {
hasPreviousSpace = true;
} else {
continue;
}
}

if (ignoreTag) {
continue;
}

if (hasOpenTag && insideKeyValue) {
printf("key: %s | value: %s\n", tagKey, tagValue);
insideKeyValue = false;
tagWritePos = 0;


tagValue = (char*) realloc(tagValue, sizeof (char));
tagValue[0] = '\0';
tagKey = (char*) realloc(tagKey, sizeof (char));
tagKey[0] = '\0';
}

if (hasOpenTag) {
tagHasData = true;
tagWritePos = 0;
continue;
}

break;
case '=':
if (ignoreTag) {
continue;
}

if (tagHasData) {
insideKeyValue = true;
tagWritePos = 0;
continue;
}
break;
default:
hasPreviousSpace = false;
break;
}

if (hasOpenTag) {
if (!tagHasData) {
currentTag = (char*) realloc(currentTag, sizeof (char) * (tagWritePos + 2));
currentTag[tagWritePos] = data;
currentTag[tagWritePos + 1] = '\0';
++tagWritePos;
} else if (!insideKeyValue) {
tagKey = (char*) realloc(tagKey, sizeof (char) * (tagWritePos + 2));
tagKey[tagWritePos] = data;
tagKey[tagWritePos + 1] = '\0';
++tagWritePos;
} else {
tagValue = (char*) realloc(tagValue, sizeof (char) * (tagWritePos + 2));
tagValue[tagWritePos] = data;
tagValue[tagWritePos + 1] = '\0';
++tagWritePos;
}
} else {
tagData = (char*) realloc(tagData, sizeof (char) * (dataWritePos + 2));
tagData[dataWritePos] = data;
tagData[dataWritePos + 1] = '\0';
++dataWritePos;
}
}

for (int i = 0; i < tagList.tagCount; i++) {
printf("%s\n", tagList.tags.name);

if (tagList.tags.dataLength != 0) {
for (int j = 0; j < tagList.tags.dataLength; j++) {
printf("keyValue: %s = %s\n", tagList.tags.data[j].key, tagList.tags.data[j].value);
}
}
if (tagList.tags.isDataTag) {
printf("%s\n", tagList.tags.tagData);
printf("\n");
}
}

fclose(sourceFile);

free(srcData);

if (tagKey != NULL) {
free(tagKey);
}

if (tagValue != NULL) {
free(tagValue);
}

if (tagData != NULL) {
free(tagData);
}

if (currentTag != NULL) {
free(currentTag);
}

if (previousTag != NULL) {
free(previousTag);
}
}
}

return 0;
}


// Clean up by freeing ressources

void cleanupApplication() {
printf("\nFreeing ressources...\n");

for (int i = 0; i < tagList.tagCount; i++) {
if (tagList.tags.tagData != NULL) {
free(tagList.tags.tagData);
}

for (int j = 0; j < tagList.tags.dataLength; j++) {
free(tagList.tags.data[j].key);
free(tagList.tags.data[j].value);
}

free(tagList.tags.name);
}

if (jsonData != NULL) {
free(jsonData);
}

if (sourceFilePath != NULL) {
free(sourceFilePath);
}

if (outputFilePath != NULL) {
free(outputFilePath);
}

printf("Clean up done. Exiting. Bye bye...\n");
}

[/src]
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Das letzte Element ist aber "tagCount" und nicht "tagCount -1" im Code - der Counter wird erst erhöht nachdem das Element angelegt ist. ;)
Oh ja, stimmt. Hatte ich übersehen. »count-1« ist ein dermaßen starkes Pattern, dass alles andere sofort wie ein Fehler aussieht. Überleg dir, ob du nicht das Array verlängern als eigene Funktion auslagern willst. Es sind zwar zwei Aktionen – Speicher (re-)allokieren und Elementzähler erhöhen –, sie bilden aber eine atomische Einheit. Die eine ohne die andere durchzuführen, macht keinen Sinn.

Sowas bietet sich immer sofort zum Wegkapseln an, weil es sonst erfahrungsgemäß nur eine Frage der Zeit ist, bis beide Verlängern-Aktionen mal *nicht* gemeinsam passieren.
 
Oben