C++: stdout/stderr umleiten intern

(DE) Projekte für OS/2, eCS und ArcaOS
(EN) OS/2, eCS and ArcaOS related projects
User avatar
LotharS
Posts: 691
Joined: Sun 29. Dec 2013, 20:07
Location: Düsseldorf

C++: stdout/stderr umleiten intern

Post by LotharS » Sat 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.

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:

User avatar
efbe
Posts: 66
Joined: Thu 11. Sep 2014, 19:33
Location: Dortmund

Post by efbe » Sat 25. Jul 2020, 20:50

LotharS wrote:
Sat 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

aschn
Posts: 984
Joined: Wed 25. Dec 2013, 22:47

Post by aschn » Sat 25. Jul 2020, 22:05

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

Oder

Code: Select all

start view xpg4ref freopen
Zuletzt geändert von aschn am Sat 25. Jul 2020, 22:20, insgesamt 1-mal geändert.
Andreas Schnellbacher

erdmann
Posts: 363
Joined: Mon 4. Jan 2016, 14:36

Post by erdmann » Sat 25. Jul 2020, 22:19

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
Posts: 363
Joined: Mon 4. Jan 2016, 14:36

Post by erdmann » Sun 26. Jul 2020, 13:23

Hier mal ein Beispielprogramm:

Code: Select all

#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.

User avatar
LotharS
Posts: 691
Joined: Sun 29. Dec 2013, 20:07
Location: Düsseldorf

Post by LotharS » Mon 27. Jul 2020, 16:14

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).

aschn
Posts: 984
Joined: Wed 25. Dec 2013, 22:47

Post by aschn » Mon 27. Jul 2020, 19:10

LotharS wrote:
Mon 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 Fri 31. Jul 2020, 11:21, insgesamt 1-mal geändert.
Andreas Schnellbacher

Martin Vieregg
Posts: 346
Joined: Tue 19. Aug 2014, 09:30

Post by Martin Vieregg » Sun 9. Aug 2020, 09:45

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: Select all

[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.

User avatar
LotharS
Posts: 691
Joined: Sun 29. Dec 2013, 20:07
Location: Düsseldorf

Post by LotharS » Mon 10. Aug 2020, 18:17

Martin Vieregg wrote:
Sun 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: Select all

[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.