Forum: Mikrocontroller und Digitale Elektronik Eigene Shell: Eingegebene Escape-Sequenzen werden von vsnprintf ignoriert?


von Alexander I. (daedalus)


Lesenswert?

Hallo,

folgender Sachverhalt: Ich habe eine recht hübsche Shell auf einem 
Controller, die über RS232 angesprochen werden kann. Hier kann man 
einfache Befehle ausführen lassen oder sich Inhalte von bestimmten 
Variablen printen lassen. Hierbei ist auch etwas Ausgabeflexibilität 
gefragt. Ein Beispiel:
1
bytes = [256] 0xAA   // Erzeugt einen Byte-Array mit 256 Elementen und Wert 0xAA
2
bytes? format 4 "%u " // Gibt den Array in 4-bytige unsigned integer Blöcke zerlegt aus, getrennt durch ein Leerzeichen
3
bytes? format 1 "0x%X," // Zeigt die Bytes im Hex-Format an und trennt sie mit einem Komma

Oben genannte Beispiele funktionieren prima, da im Hintergrund die 
Funktion vsnprintf() arbeitet, die den Formatstring (z.B. "0x%X") 
entgegennimmt und auf die Daten der Shell-Variable "bytes" in args 
anwendet und das Ergebnis im screenbuffer ablegt:
1
vsnprintf(screenbuffer, 100, format, args);

Wenn ich jetzt aber möchte, dass mir immer nur ein Wert pro Ausgabezeile 
ausgegeben wird, erscheint folgende Eingabe logisch, funktioniert aber 
nicht, wie von mir erwartet:
1
bytes? format 4 "%u\r\n"

Jetzt wird keine neue Zeile begonnen, sondern der String "\r\n" genau so 
mit ausgegeben und alle Ergebnisse aneinander gehängt, was nicht im 
Sinne des Erfinders liegt.

Warum reagiert vsnprintf hier so und interpretiert die eingegeben 
Escape-Sonderzeichen nicht? Oder liegt's vielleicht am Terminalprogramm?

Wenn ich im Source-Code
1
vsnprintf(screenbuffer, 100, format, args);
ersetze durch
1
vsnprintf(screenbuffer, 100, "%u\r\n", args);
kommen die Zeilenumbrüche wie erwartet.

Wie bringe ich vsnprintf dazu, meine eingegebenen Escape-Zeichen zu 
interpretieren anstatt sie nur zur Ausgabe durchzureichen?

von Peter II (Gast)


Lesenswert?

um welche Prorgrammiersprache geht es denn hier?
> bytes = [256] 0xAA
> bytes? format 4 "%u "

also C ist das schon mal nicht

> vsnprintf(screenbuffer, 100, format, args);
das sieht zwar wie C aus, aber was steht nun in format drin?

von Alexander I. (daedalus)


Lesenswert?

Achso, sorry.

Die Shell hat ihre eigene Syntax und sollte hier keine Rolle spielen, 
die Argumente werden geparset und zerlegt und an die C-Funktion 
vsnprintf weitergegeben.
1
vsnprintf(screenbuffer, 100, format, args);

In der C-Variable "format" steht das in der Shell eingegebene Argument 
"%u\r\n" drin, in C-Variable "args" ist der Zeiger auf den Inhalt der 
Shell-Variable "bytes" enthalten. Die Variableninhalte von format und 
args habe ich mit dem Debugger überprüft, die Argument-Zerlegung durch 
die Shell hat korrekt funktioniert.

von Karl H. (kbuchegg)


Lesenswert?

Alexander I. schrieb:

> Warum reagiert vsnprintf hier so und interpretiert die eingegeben
> Escape-Sonderzeichen nicht?

Du hast etwas noch nicht verinnerlicht:

Der String
"\r\n"

enthält NICHT die Zeichen '\' dann 'r', dann '\' und 'n'.

\n ist lediglich in der Programmiersprache C die Schreibweise für ein 
ganz bestimmtes Zeichen, welche eben in der Programmiersprache durch 
hintereinanderfügen der beiden Zeichen \ und n geschrieben wird. Aber es 
ist im String lediglich ein einziges Zeichen (für das es auf der 
Tastatur keine Taste gibt, daher der Ausweg über die \ Schreibweise. Der 
C-Compiler fischt dann das \ mit dem nachfelgenden n heraus und ersetzt 
diese Sequenz durch den ASCII Code des Zeichens Carriage Return)

Wenn du aber einen Text per RS232 (oder sonstwo) her bekommst, dann 
enthält der String tatsächlich einfach nur die beiden Zeichen \ und n, 
und zwar im String.

Auf ASCII Code Ebene sieht man das noch besser.

Der String, den du in C schreibst,
"\r\n"

wird vom Compiler im Speicher so abgelegt
0x0D 0x0A 0x00

gibts du aber über deine Command Line den Text "\r\n" ein, dann wird der 
so im Speicher abgelegt

0x5C 0x72 0x5C 0x6E 0x00

etwas völlig anderes. (In der Programmiersprache würdest du das als 
"\\r\\n" schreiben)

Wenn du haben willst, dass in einem String, den dir der Benutzer 
vorgegben hat, ein "\r" zum Zeichen '\r' umgewandelt wird (bzw "\n" zu 
'\n') dann musst du das selber entsprechend umformen. Also genau das 
tun, was auch der C-Compiler tut, wenn er dein Programm übersetzt und in 
einem String auf die Sequenz "\r" (oder sonst irgendeine andere Escape 
Sequenz) stösst.


> Warum reagiert vsnprintf hier so und interpretiert die
> eingegeben Escape-Sonderzeichen nicht?

Weil vsnprintf in der Beziehung da überhaupt nichts mehr interpretiert. 
In einem C-Programm wird die entsprechende Substituierung bereits vom 
C-Compiler erledigt. Und das macht auch Sinn, denn schliesslich soll die 
strlen("\r\n") ja 2 sein und nicht 4.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der
> C-Compiler fischt dann das \ mit dem nachfelgenden n heraus und ersetzt
> diese Sequenz durch den ASCII Code des Zeichens Carriage Return)

LF, nicht CR :-)

von Alexander I. (daedalus)


Lesenswert?

Hallo KHB,

ich hatte es fast befürchtet, dass der Precompiler hier seine Finger im 
Spiel hat. Dann werde ich meine Argumenterfassung der Shell entsprechend 
anpassen müssen, damit diese ein CR oder LF-Zeichen erzeugt. :-/ Hatte 
gehofft da gäbe es vielleicht was Hübsches in stdio was diese Aufgabe 
zur Laufzeit übernehmen kann...

von Karl H. (kbuchegg)


Lesenswert?

Alexander I. schrieb:
> Hallo KHB,
>
> ich hatte es fast befürchtet, dass der Precompiler hier seine Finger im
> Spiel hat.

Nix Precompiler. Das ist schon der echte Compiler.

> Dann werde ich meine Argumenterfassung der Shell entsprechend
> anpassen müssen, damit diese ein CR oder LF-Zeichen erzeugt. :-/ Hatte
> gehofft da gäbe es vielleicht was Hübsches in stdio was diese Aufgabe
> zur Laufzeit übernehmen kann...

Ah, geh. Ist doch trivial. Einfache Zeichenersetzung, in einer Schleife 
den String durchgehen und nach einem '\' ja nachdem nächsten Zeichen 
einen anderen ASCII Code einsetzen. Da der String dabei nur kürzer 
werden kann, kann man das auch inplace machen.

Aber Achtung: Diese Substituierung darfst du nur bei Dingen machen, die 
du in deiner Shell als String klassifiziert hast!

  \a                  Bell (alert)
  \b                  Backspace
  \f                  Formfeed
  \n                  New line
  \r                  Carriage return
  \t                  Horizontal tab
  \v                  Vertical tab
  \'                  Single quotation mark
  \"                  Double quotation mark
  \\                  Backslash
  \?                  Literal question mark
  \ooo                ASCII character in octal notation
  \xhh                ASCII character in hexadecimal notation

Die letzten 3 kannst du dir höchst wahrscheinlich sparen und den 
'vertical tab' wüsste ich jetzt auch nicht, wozu der gut sein soll.

von Alexander I. (daedalus)


Lesenswert?

Hat super geklappt. Vielen Dank!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.