Binmode STDOUT



  • Einen wunderschönen Guten Morgen,
    Mein CGI/c++ entwickle ich auf Windows 10 und compiliere die Sourcen auch auf einem Shared Host (keine Ahnung welches OS genau da läuft, irgendein Linux cat /etc/issue gibt es nicht her, ist auch egal). Nun ist es so daß

        string bin = my::slurp_file("/var/www/vhosts/rolfrost.de/httpdocs/red.gif");
        cout << "Content-Type: image/gif\n\n" << bin;
    

    auf dem Shared Host einwandfrei funktioniert weil vom OS her STDIN im binmode eingestellt ist. Auf Win 10 jedoch ist das nicht so. Hinzuzufügen ist noch, daß bin.length() stimmt, das habe ich geprüft. Das Einlesen der Binary auf einen string ist also nicht das Problem, sondern der stdout Modus. Wie kann ich den auf binmode setzen?

    MFG und Danke im Vorab.



  • Den kannst du garnicht setzen, weil std::cout lesbare Zeichen und keine Rohdaten auf der Konsole ausgibt, zumindest mit den Stream-Operatoren.

    Ungetestet:
    std::ostream bietet die Funktion write an, die einen Zeiger und eine Länge akzeptiert. Du könntest also das hier mal probieren:

    std::string const bin_data = my::slurp_file( "..." );
    if( !bin_data.empty() )
    {
       std::cout << "Content-Type: image/gif\n\n";
       std::cout.write( bin_data.data(), bin_data.size() );
    }
    

    std::string finde ich hier als Datentyp für den Dateiinhalt unpassend, weil's ja in diesem Fall GIF-Binärdaten sind. std::vector<char> ist da der bessere Datentyp, auch wenn's mit std::string funktioniert. Der Datentyp std::string drückt aus, dass es sich bei den Daten um Text handelt, auch wenn std::string mit Binärdaten umgehen kann.

    Edit:
    Vermutlich erzähle ich hier Quatsch, hatte da sowas wie std::cout << 9 vor Augen, wo die Ziffer 9 und nicht der Dezimalwert 9 ausgegeben wird. Der Stringinhalt ist ja der gleiche, es dürfte eigentlich keinen Unterschied machen, ob man ihn mit << oder write ausgibt.
    Erklär doch bitte mal, was du erwartest und was das Programm macht.



  • @DocShoe
    Jo - ich bin auch unsicher, was string/cout mit 0x0a und 0x0d machen würden.



  • @DocShoe

    mit write wird das auch nichts das habe ich schon probiert. Das Problem ist, wie gesagt nur lokal auf Win 10. Ich sehe das übrigens auch daran, daß Inhalte einer <textarea> anders ankommen wenn das HTML nicht im Binmode rausgeht. Das war aber auch schon immer so und mit Perl kein Problem generell den Binmode zu setzen.

    MFG



  • @DocShoe

    ist schon richtig was Du schriebst. Also ich könnte mit setmode(STDOUT_FILENO, O_BINARY); setzen und #include <fcntl.h> als Präpozessor Anweisung setzen. Da müsste ich aber jedesmal vor dem Hochladen die Source ändern und das wäre äußerst unschön. Denn der Shared Host Compiler kennt das alles nicht obwohl die c++ Version dieselbe ist-

    MFG



  • Beschreib doch bitte mal genau, was du erreichen möchtest. Was erwartest du, und was ist das aktuelle Ergebnis? Soweit ich verstanden habe verhält sich dein Code auf dem Zielsystem anders als auf dem Testsystem, aber wie macht sich der Unterschied bemerkbar?



  • @_ro_ro sagte in Binmode STDOUT:

    @DocShoe

    mit write wird das auch nichts das habe ich schon probiert. Das Problem ist, wie gesagt nur lokal auf Win 10. Ich sehe das übrigens auch daran, daß Inhalte einer <textarea> anders ankommen wenn das HTML nicht im Binmode rausgeht. Das war aber auch schon immer so und mit Perl kein Problem generell den Binmode zu setzen.

    MFG

    Öhm kann es sein, dass der text in utf-8 codiert ist? Denn das funktioniert unter windows nicht by default.
    Dort wird nicht utf-8 für std::cout verwendet sondern eine ANSI Kodierung. Bzw. das "Terminal" (cmd.exe, powershell und co) interpretieren die Daten in der eingestellten ANSI Kodierung.



  • @DocShoe sagte in Binmode STDOUT:

    Beschreib doch bitte mal genau, was du erreichen möchtest. Was erwartest du, und was ist das aktuelle Ergebnis? Soweit ich verstanden habe verhält sich dein Code auf dem Zielsystem anders als auf dem Testsystem, aber wie macht sich der Unterschied bemerkbar?

    Ganz einfach: Lokal (Win10) sind Grafiken und PDF-Dateien kaputt und die Zeilenumbrüche bei mehrzeiligen Eingaben in eine <textarea> werden falsch umgesetzt. Serverseitig passiert das nicht, weil das OS generell den binmode setzt.

    Ich habe jetzt eine ganz banale Lösung damit ich meine Hauptdatei nicht ändern muß vor dem Deployment:

    #include "binmode.c" 
    
    int main(){
       binmode();
    ...
    

    Und in der Datei serverseitig ist die Funkktion hohl. Lokal steht drin

    #include <fcntl.h>
    void binmode(){
        setmode(STDIN_FILENO, O_BINARY);
        setmode(STDOUT_FILENO, O_BINARY);
    }
    

    Und so sieht das Ergebnis lokal ganz genaus aus wie beim Provider. Ich muss nur aufpassen daß ich die Datei binmode.c nicht aus Versehen hochlade.

    MFG



  • @firefly

    Nee, mit UTF-8-kodierten Texten gibt es generell keine Problems. Die werden einfach so rausgegeben wie sie reinkommen, egal ob User-Input oder aus meinen Dateien. Auch in der cpp-Hauptdatei stehen utf-8-kodierte Literale, ein cout << "Fritz Müller"; wird natürlich nur utf-8-kodiert ausgegeben wenn die .cpp Datei auch als utf-8-kodiert gespeichert wurde (was bei mir selbstverständlich der Fall ist).

    MFG

    Demo UTF-8 in C++:
    http://rolfrost.de/form.chtml



  • Ok, dann versuche ich nicht, das weiter zu verstehen.

    Du kannst aber sowas bauen, wenn der Code nur für Windows-Plattformen ausgeführt werden soll (entsprechendes Makro prüfen, heißt das bei dir _WINDOWS oder anders):

    #ifdef _WINDOWS
       #include <io.h>
       #include <fnctl.h>
    #endif
    
    
    void set_file_binary_mode()
    {
    #ifdef _WINDOWS
       _setmode( _fileno( stdin  ), _O_BINARY  );
       _setmode( _fileno( stdout ), _O_BINARY );
    #endif
    }
    

    Edit: Quelltext nach MS-Doku angepasst



  • @DocShoe sagte in Binmode STDOUT:

    Ok, dann versuche ich nicht, das weiter zu verstehen.

    Du kannst aber sowas bauen, wenn der Code nur für Windows-Plattformen ausgeführt werden soll (entsprechendes Makro prüfen, heißt das bei dir _WINDOWS oder anders):

    #ifdef _WINDOWS
       #include <fnctl.h>
    #endif
    
    
    void set_file_binary_mode()
    {
    #ifdef _WINDOWS
       _setmode( STDIN_FILENO, O_BINARY );
       _setmode( STDOUT_FILENO, O_BINARY );
    #endif
    }
    

    Gute Idee!!!

    Danke!!

    PS: _WIN32



  • @_ro_ro sagte in Binmode STDOUT:

    @DocShoe sagte in Binmode STDOUT:

       _setmode( STDOUT_FILENO, O_BINARY );
    

    Gute Idee!!!

    Und? Funktioniert das? Irgendwie habe ich solche Tools bisher nur für Linux geschrieben, aber es wäre gut, wenn sowas dann ohne viel Aufwand auch unter Windows laufen würde. Diese Datenverarbeitungs-Pipelines, die man damit via Kommandozeile zusammenstöpseln kann sind ja schon extrem praktisch:

    cat file | tool1 | tool2 | tool3 > processed_file



  • @_ro_ro Verstehe ich dein Anliegen richtig, dass du das Gif als Binary direkt ins HTML schreiben möchtest? Ich würde mutmaßlich, das Gif in eine extra Datei schreiben und die Datei ins HTML einbinden.

    Deine Lösung gefällt mir persönlich nicht. Das scheint irgendwie speziell auf die beiden Systeme mit denen du arbeitest zugeschnitten zu sein ohne das du garantieren kannst, dass das auf einem dritten System jetzt auch funktioniert.

    Was du sonst versuchen kannst, ist, durch den String zu iterieren und aus einzelnen Chars ein std::bitset machen und das mit to_string() dann konvertieren.

    Wo ich gerade nach Binary Darstellungen von Bildern in HTML google, willst du das wirklich als Binary darstellen? Ich finde nur Base64 encodierte ASCII String representation, aber ich bin in Webtechnologien auch echt schlecht.



  • @Schlangenmensch sagte in Binmode STDOUT:

    @_ro_ro Verstehe ich dein Anliegen richtig, dass du das Gif als Binary direkt ins HTML schreiben möchtest?

    Nein. Und es geht ja auch nicht nur um Grafiken oder PDF oder Zip-Dateien oder sonstige Binaries die nach stdout sollen, sondern auch darum eine HTML-<textarea> im Binmode auszuliefern, da ansonsten die Zeilenumbrüche der Eingaben falsch gesendet werden bei einem Submit via POST. Also einen generellen binmode.

    Wo ich gerade nach Binary Darstellungen von Bildern in HTML google, willst du das wirklich als Binary darstellen? Ich finde nur Base64 encodierte ASCII String representation, aber ich bin in Webtechnologien auch echt schlecht.

    Jeder Webserver sendet Grafiken als Binary, ein Request like <img src="/red.gif"> weist bspw. den
    Webserver an, diese Datei im DOCUMENT_ROOT zu lesen und als Binary via HTTP zu senden.

    MFG



  • @Schlangenmensch sagte in Binmode STDOUT:

    ...
    Was du sonst versuchen kannst, ist, durch den String zu iterieren und aus einzelnen Chars ein std::bitset machen und das mit to_string() dann konvertieren.
    ...

    Das Problem scheint zu sein, dass unterschiedliche Streams zum Lesen und Schreiben der Daten benutzt werden. Wenn die Datei im Binär-Modus gelesen wird landet sie 1:1 im Speicher, beim Schreiben in die Standardausgabe ersetzt das std::cout Objekt unter Windows LF durch CR+LF, was die Binärdaten zerstört (btw: Da hätte ich von @_ro_ro auch gerne mal zwei kurze Hex-Dumps gesehen, die das Problem zeigen). @_ro_ro sucht eine Möglichkeit, diese automatische Übersetzung zu deaktivieren. Das Verhalten steckt iwo im std::cout Objekt, und da hört mein Wissen auf. Ich weiß nicht, ob man das durch das Benutzen einer eigenen locale oder stream_buf steuern kann, da müssen jetzt Leute her, die sich damit auskennen 😉

    Edit:
    Hab mir mal die MSVC Implementierung auf github angeschaut, da werden std::cinund std::cout über einen filebuf um stdin und stdout gebaut. Zusammen mit der _setmode Funktion bedeutet dass, dass das Verhalten schon in stdin und stdout steckt und unter C++ vllt überhaupt nicht bekannt ist und damit auch nicht verändert werden kann, sondern nur die zu Grunde liegenden stdin und stdout Objekte.



  • @DocShoe

    die von mir beschiebenen Effekte treten nur unter Win32 auf. XP ging sogar soweit, Dateien spontan zu verändern wenn nicht explizit im BINARY-Mode (sysopen call) darauf zugegriffen wurde. Und als ich vor 10 Jahren mal einen Parser für den grottigen Enctype multipart/form-data in C geschieben habe, musste ich auch feststellen daß C unter Windows per Default keinen Binmode stdin/stdout kennt. der also explizit gesetzt werden muss. In Perl ist das eine Zeile binmode STDOUT und gut isses.

    Offensichtlich entwickelt Ihr nicht unter Win32.

    MFG



  • @_ro_ro sagte in Binmode STDOUT:

    @DocShoe
    Offensichtlich entwickelt Ihr nicht unter Win32.

    Steile These



  • @_ro_ro Hm, irgendwie verstehe ich deine Architektur noch nicht. Du baust in C++ dein HTML zusammen, schreibst das in die Konsole, und liest das dann von da aus mit einer anderen Software ein, die dann die Webseite ausliefert?

    Ich frage, weil ich nie auf die Idee gekommen wäre, eine Datei nach std::out zu schreiben

    @_ro_ro sagte in Binmode STDOUT:

    Offensichtlich entwickelt Ihr nicht unter Win32.

    Doch, professionell momentan sogar ziemlich ausschließlich. Aber ich schreibe keine Dateien in die Konsole, hängt dann ja auch noch davon ab, welche Konsole verwendet wird und wie die die Daten interpretiert.



  • @Schlangenmensch

    Ein CGI-Programm liest von STDIN und schreibt nach STDOUT. Das ist alles was ein CGI-Programm können muss 😉

    Guck Dir mal den Standard CGI/1.1 an.

    MFG



  • @Schlangenmensch

    Bei CGI Anwendungen sind cin/cout keine Konsolenstreams, sondern umgeleitete Streams des Webservers. Der leitet seine STDOUT Ausgaben in STDIN der Anwendung um und liest über sein STDIN die Daten, die die CGI Anwendung in ihren STDOUT schreibt. Das hat mit der DOS-Konsole unter Windows nichts mehr zu tun, sondern ist ein IPC Mechanismus

    (Webserver-Anwendung::stdout) => (CGI::stdin) => (CGI::stdout) => (Webserver-Anwendung::stdin)


Anmelden zum Antworten