C++: stdout/stderr umleiten intern

(DE) Projekte für OS/2, eCS und ArcaOS
(EN) OS/2, eCS and ArcaOS related projects
Antworten
Benutzeravatar
LotharS
Beiträge: 968
Registriert: So 29. Dez 2013, 20:07
Wohnort: Düsseldorf
Kontaktdaten:

C++: stdout/stderr umleiten intern

Beitrag von LotharS »

Die Aufgabe: ich möchte den Output eines Kommandozeilenprogramms zur weiteren Auswertung intern umleiten.
Lässt sich in REXX elegant mittels RxQueue lösen.

Zu C++ habe ich bereits die 'system()'-Funktion gelernt. Das "cmd"-Argument darin ließe sich ja so schreiben, dass der Output auf eine Temp-Datei umgeleitet wird zum Wiedereinlesen, ok. Nach zwei vollen Tagen googlen habe ich nur herausgefunden, dass es intern mit einer extra Pipe gehen solle (soweit jedenfalls mindestens 1001 Linuxer-Beiträge..., Stichwort 'dort popen()').
Müsste in OS/2 doch eigentlich auch irgendwie mithilfe 'DoSCreatePipe' usw. gehen, REXX schafft es ja auch. Nur fehlt mir noch der passende Schlüssel :ugeek: Wo könnte ich dazu schlauer werden?

Nur nebenbei: Wohl liegt im Netz ein "OS/2-popen()"-Schnipsel, aber mit welchem Compiler und Tookit der Autor das wohl erstmal übersetzt bekommen haben mag??... Sprang dabei wenigstens ein Bugreport für bww raus :geek:
Benutzeravatar
efbe
Beiträge: 69
Registriert: Do 11. Sep 2014, 19:33
Wohnort: Dortmund

Beitrag von efbe »

LotharS hat geschrieben: Sa 25. Jul 2020, 16:09 Die Aufgabe: ich möchte den Output eines Kommandozeilenprogramms zur weiteren Auswertung intern umleiten.
Lässt sich in REXX elegant mittels RxQueue lösen.

Nur nebenbei: Wohl liegt im Netz ein "OS/2-popen()"-Schnipsel, aber mit welchem Compiler und Tookit der Autor das wohl erstmal übersetzt bekommen haben mag??... Sprang dabei wenigstens ein Bugreport für bww raus :geek:
Vielleicht hilft dir die OpenWatcom doku mit Beispielcode zu pipe und popen in clib.pdf oder *.inf.
Ist aber soweit ich sehe C nicht C++.

Gruß
Frank
Benutzeravatar
aschn
Beiträge: 1363
Registriert: Mi 25. Dez 2013, 22:47

Beitrag von aschn »

Vielleicht hilft Dir auch so etwas wie tee. Das Beispiel benutzt getchar, putchar und putc.

Oder

Code: Alles auswählen

start view xpg4ref freopen
Zuletzt geändert von aschn am Sa 25. Jul 2020, 22:20, insgesamt 1-mal geändert.
Andreas Schnellbacher
erdmann
Beiträge: 594
Registriert: Mo 4. Jan 2016, 14:36

Beitrag von erdmann »

Ja, geht mit DosCreatePipe sowie DosDupHandle. Das Programm startet man mit DosStartSession und wartet dann auf das Programmende mit DosReadQueue. Such mal im Internet. Ansonsten habe ich auch Beispielcode. Das hat dann aber nicht mehr viel mit C++ zu tun.
erdmann
Beiträge: 594
Registriert: Mo 4. Jan 2016, 14:36

Beitrag von erdmann »

Hier mal ein Beispielprogramm:

Code: Alles auswählen

#define INCL_BASE
#include <os2.h>


#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>

#define TOOLNAME     "IPFC.EXE\0"

#define HF_STDSCREEN 1
//#define PIPESIZE     65535 /* the maximal value for an unnamed pipe to function correctly */
#define PIPESIZE     128
#define LINELENGTH   CCHMAXPATH + 260


int main(int argc, char *argv[])
{
   HFILE hw,hr;
   HFILE herr = HF_STDSCREEN;
   HFILE hsav = -1;
   int res = -1;
   APIRET ulrc;
   ULONG ulRead,ulWritten,ulLen;
   PCHAR gMessage;
   CHAR szCmdLine[CCHMAXPATH];

   if (DosAllocMem(  (PPVOID)&gMessage,
                     PIPESIZE,
                     PAG_READ|PAG_WRITE|PAG_COMMIT) != NO_ERROR) {
      return -1;
   }
   *gMessage = '\0';
   szCmdLine[0] = '\0';

   if (DosCreatePipe(   &hr,
                        &hw,
                        PIPESIZE) == NO_ERROR) {

      ulrc = DosDupHandle(HF_STDSCREEN,&hsav);
      ulrc = DosDupHandle(hw,&herr);

      strcpy(szCmdLine,TOOLNAME);
      PSZ pszTemp = &szCmdLine[0] + strlen(szCmdLine) + 1;
      for (ULONG i=1;i<argc;i++) {
         pszTemp += sprintf(pszTemp,"%s ",argv[i]);
      }
      *(pszTemp-1)='\0';
      *(pszTemp)='\0';

      RESULTCODES sCodes={0};
      PID pid;

      CHAR pszFileStem[CCHMAXPATH];
      CHAR pszFileExt[CCHMAXPATH];
      ULONG ulLineNumber;
      CHAR pszErrorText[CCHMAXPATH];
      CHAR pszLineIn[LINELENGTH];
      ULONG j;
      BOOL fLine;

      ulrc = DosExecPgm(NULL,0,EXEC_ASYNCRESULT,szCmdLine,NULL,&sCodes,TOOLNAME);

      ulrc = DosClose(hw);
      ulrc = DosDupHandle(hsav,&herr);
      ulrc = DosClose(hsav);

      j=0;
      fLine = FALSE;
      pszLineIn[0]='\0';

      ulrc = DosRead(hr,gMessage,PIPESIZE,&ulRead);
      while (ulRead) {
         gMessage[ulRead] = '\0';
         for (i=0;i < ulRead;i++) {
            if (gMessage[i] == '\n' || gMessage[i] == '\r' || gMessage[i] == '\0') {
               pszLineIn[j] = '\0';
               if (j) {
                  j = 0;
                  int num = sscanf(pszLineIn, "<%[^.]%[^:]:%lu> %[^\r\n]",pszFileStem,pszFileExt,&ulLineNumber,pszErrorText);
                  if (num == 4) {
                     ulLen = sprintf(pszLineIn,"%s%s(%lu): %s\r\n",pszFileStem,pszFileExt,ulLineNumber,pszErrorText);
                  }
                  else {
                     ulLen = sprintf(pszLineIn,"%s\r\n",pszLineIn);
                  }
                  DosWrite(HF_STDSCREEN,pszLineIn,ulLen,&ulWritten);
               }
            }
            else {
               pszLineIn[j++] = gMessage[i];
            }
         }
         ulrc = DosRead(hr,gMessage,PIPESIZE,&ulRead);
      }
      ulrc = DosClose(hr);

      ulrc = DosWaitChild(DCWA_PROCESSTREE,DCWW_WAIT,&sCodes,&pid,sCodes.codeTerminate);
      res = sCodes.codeResult;
   }
   ulrc = DosFreeMem(gMessage);
   return res;
}
Wenn man "stderr" auch umleiten will tut man das analog mit einer zweiten Pipe und "2" (statt 1) als standard Dateihandle. Übrigens kann die Pipegröße doch viel größer gewählt werden als ich dachte, auch problemlos 1 MB und größer.
Benutzeravatar
LotharS
Beiträge: 968
Registriert: So 29. Dez 2013, 20:07
Wohnort: Düsseldorf
Kontaktdaten:

Beitrag von LotharS »

Großen Dank erstmal Euch allen :D Da hab' ich einiges zu verdauen, aber is' halt so mit "Neuem" :geek:
@Lars: ich glaube sogar, ich habe die Idee soweit verstanden. Natürlich nicht alle Feinheiten, Respekt!
Wenn man "stderr" auch umleiten will
.. Fällt mir dabei ein: PM-Tools lenken stderr gerne auf eine eigene "Konsole" um. Gibt es dafür einen bekannten Standard oder denkt sich jeder Entwickler sein eigenes Fensterchen aus? Wäre bei Java und Web von Hause aus dabei...
Das hat dann aber nicht mehr viel mit C++ zu tun.

Macht ja nichts. Man kann ja notfaölls C++-stilgerechte Wrapper packen... Zeitweise Beschäftigung mit Java und OO-Design in PHP war "strenge Erziehung", mag ich nur empfehlen (Anwendungen: unbedingt, Zeitkritisches/Treiber: eher kaum).
Benutzeravatar
aschn
Beiträge: 1363
Registriert: Mi 25. Dez 2013, 22:47

Beitrag von aschn »

LotharS hat geschrieben: Mo 27. Jul 2020, 16:14 PM-Tools lenken stderr gerne auf eine eigene "Konsole" um. Gibt es dafür einen bekannten Standard
Ich glaub, wenn Du die Exe als VIO-Typ erstellst, also nicht WINDOWAPI, sondern WINDOWCOMPAT, dann werden printfs (oder auch entsprechende C++-Befehle) im VIO-Fenster ausgegeben. Evtl. musst Du auch die Exe aus einem VIO-Fenster heraus starten. Wenn Du das nicht haben willst, dann ist es recht verbreitet dafür PMPrintf zu verwenden. Die neueste Version kommt von Andi.

Du kannst pmprintf.h/pmprintf.lib/pmprintf.dll oder auch pmprintf.c (findet man in der Hilfe) aus dem Paket oben verwenden oder die Originalversion printf.c von Mike Cowlishaw. Die ist auch z.B. bei jedem Projekt von Christian dabei, z.B. hier.
Zuletzt geändert von aschn am Fr 31. Jul 2020, 11:21, insgesamt 1-mal geändert.
Andreas Schnellbacher
Martin Vieregg
Beiträge: 459
Registriert: Di 19. Aug 2014, 09:30

Beitrag von Martin Vieregg »

Du brauchst nur Pipes, wenn Du gleichzeitig den Output erzeugen und gleich weiterverarbeiten willst. Sonst spricht nichts gegen eine temporäre Datei. Sonst kannst Du einfach das Kommandozeilenzeichen > verwenden. Vergess nicht stderr. Um sowohl stdout als auch stderr in eine Datei umzuleiten, gibt man ein:

Code: Alles auswählen

[C:\] dir >output.txt 2>&1
Dann wird stderr zuerst nach stdout geleitet und dann stdout (incl. stderr) in die Datei.

Im Fall von Pipes muss man beides bearbeiten. MeShell arbeitet mit named pipes. Sowohl für den Empfang von stdout als auch von stderr gibt es einen eigenen Thread.
Benutzeravatar
LotharS
Beiträge: 968
Registriert: So 29. Dez 2013, 20:07
Wohnort: Düsseldorf
Kontaktdaten:

Beitrag von LotharS »

Martin Vieregg hat geschrieben: So 9. Aug 2020, 09:45 Du brauchst nur Pipes, wenn Du gleichzeitig den Output erzeugen und gleich weiterverarbeiten willst.
Genau das ist die Absicht. Auf temporäre Datei schreiben und wieder einlesen ginge mir bei zufällig umfangreicherem stdOut-put zu langsam.

Dank Lars' Code-Beispiel - nochmals DANKE! - habe ich sogar schon einen Prototyp hingekriegt, verpackt in C++-OO-Module (4 eigene Klassen :ugeek: ). Nach recht tiefer Lernkurve im Probetrainig... :roll:
Der ist zwar nicht so universell wie ich anfangs erträumte, wird sich jedoch für meine nächsten Schritte verwenden lassen, und besser als erhofft (suspekt - wo sind die Fallen... :shock: ) Für die Hitze heute bin ich mit 'nem (unaufgeräumten) Test-Resultat zufrieden, betraf "htext201.zip" (eines meiner Lieblings-Tools...):

Code: Alles auswählen

[G:\lscpp\lview]lmain
-->CreatePipe READ=3 WRITE=5
-->CreatePipe READ=6 WRITE=7
00>CreateView Beginn ------
-->1hier Stdout
-->1hier Stderr
w1>Close WRITE handle=0
w2>Close WRITE handle=0
-->2hier Stdout
Read-Handle=3
Read-RC--vor=0
Read-RC-nach=0
+1>Zeilen in Queue   =39
r1>Close READ  handle=0

A1>Zeilen in Queue: 39
A2>Zeilen in Queue: 0
A2>Zeilen in Bag  : 35
B1>Zeilen in Set0 : 7
B2>Zeilen in Set1 : 28
C> CreateView fertig ------

Create View - test results:
-: --Length -EAs  ------Date -Time  Name
1:      372    0  20.12.2015 16:47  file_id.diz
2:    13596    0  29.11.2014 15:28  htcmp.cmd
3:   128757    0  20.12.2015 16:34  htext.cmd
4:   116930    0  20.12.2015 16:51  htext.inf
5:    19625    0  27.07.2008 22:00  htlaunch.exe
6:    72337    0  29.11.2014 15:26  ipf2htext.cmd
7:      246    0  27.07.2008 22:00  readme.cmd
8:        0    0  29.11.2014 15:13  bmp/
9:      218    0  27.07.2008 22:00  bmp/book.bmp
10:     1326    0  27.07.2008 22:00  bmp/ns.bmp
11:     1326    0  27.07.2008 22:00  bmp/nsmail.bmp
12:        0    0  20.12.2015 16:52  ipf/
13:    59067    0  27.07.2008 22:00  ipf/commands.inc
14:        0    0  20.12.2015 14:08  ipf/common/
15:      379    0  27.07.2008 22:00  ipf/common/history.eng
16:      349    0  27.07.2008 22:00  ipf/common/trademarks.eng
17:       98    0  27.07.2008 22:00  ipf/common/vars.eng
18:     1791    0  20.12.2015 14:50  ipf/copyright.eng
19:     1130    0  27.07.2008 22:00  ipf/example.bmp
20:     6303    0  27.07.2008 22:00  ipf/footnote.inc
21:     4929    0  27.07.2008 22:00  ipf/hints.inc
22:     8790    0  20.12.2015 16:42  ipf/history.inc
23:      592    0  20.12.2015 14:08  ipf/htext.txt
24:     3348    0  27.07.2008 22:00  ipf/installation.inc
25:     3415    0  30.08.2008 00:23  ipf/limitations.inc
26:    16263    0  20.12.2015 16:26  ipf/links.inc
27:      201    0  27.07.2008 22:00  ipf/lol.inc
28:     1817    0  27.07.2008 22:00  ipf/overview.inc
29:      544    0  27.07.2008 22:00  ipf/prerequisites.inc
30:     8969    0  27.07.2008 22:00  ipf/samples.inc
31:     3051    0  27.07.2008 22:00  ipf/sublink.inc
32:      249    0  20.12.2015 14:50  ipf/title.inc
33:      208    0  29.11.2014 15:20  ipf/urls.inc
34:    13292    0  27.07.2008 22:00  ipf/usage.inc
35:       27    0  27.07.2008 22:00  ipf/vars.inc
+:   489545    0                 35 files

[G:\lscpp\lview]
Im Fall von Pipes muss man beides bearbeiten. MeShell arbeitet mit named pipes. Sowohl für den Empfang von stdout als auch von stderr gibt es einen eigenen Thread.
Ich benötige nur die Weiterleitung von stdout lokal nach "oben".
Stderr verschwindet in meinem Prototyp im Nirvana: ich denke ich brauch's eh' nicht, und meine Tests mit zweiter Pipe endeten in Hängern bzw. beim Debuggen mit "The system detected an external processing error at..." --> Kaltstart :cry: Nostalgie... Reicht mir erstmal.
Antworten