Discussion:
Another interesting program.
Add Reply
wij
2024-11-26 05:25:55 UTC
Reply
Permalink
C++ was once? an advanced language for system programming. Now what? As usual, it
wants more, so the answer should be yes it still is.

I just rewrote an example program:

---- a_ptytest.cpp
/* Copyright is licensed by GNU LGPL, see file COPYING. by I.J.Wang 2008

Convert the example program ptytest.c in Linux Application Development
[Michael K.Johnson, Erik W. Troan] p400. See the book for details.
See also
[Advanced Programming in the UNIX(r) Environment 2nd edition, W.R.S, S.A.R.]

Type 'exit' (or Cntrl-C) in the new pty to exit program.

Build: make a_ptytest
*/
#include <Wy.stdio.h>
#include <Wy.unistd.h>
#include <Wy.termios.h>
#include <Wy.select.h>
#include <Wy.signal.h>
#include <Wy.pty.h>

using namespace Wy;

void sigwinch_handler(int,::siginfo_t*,void*)
{
};

void t0() {
Errno r;
SigAct act(sigwinch_handler,Wy::SigSet(),SA_NOCLDWAIT);
ByteFlow master;
ProcessID cpid;
String name;

if((r=forkpty(master,cpid,name))!=Ok) {
WY_THROW(r);
}

if(cpid==0) {
::execl("/bin/sh", "/bin/sh", NULL);
::exit(-1);
}

// Parent process
FdSet rdset,errset;
rdset.set(cin);
rdset.set(master);
errset.set(master);

for(;;) {
FdSet rset(rdset),eset(errset);
char buf[512];
size_t n_rw;

r=pselect(&rset,NULL,&eset,NULL,NULL);
if((r!=Ok)&&(r!=EINTR)) {
cerr << wrd(r) << WY_ENDL;
break;
}
if(eset.contain(master)) {
WY_THROW( Errno() );
}
if(rset.contain(master)) {
if((r=master.read(buf,WY_CARRLEN(buf),n_rw))!=Ok) {
// may cause EIO while reading controllong terminal
if(r!=EIO) {
WY_THROW(r);
}
}
if(n_rw==0) {
break;
}
if((r=cout.write(buf,n_rw,n_rw))!=Ok) {
WY_THROW(r);
}
}

if(rset.contain(cin)) {
String str;
cin >> str;
master << str;
};
};

};

int main(int argc, const char* argv[])
try {
t0();
cout << "OK" WY_ENDL;
return 0;
}
catch(const Errno& e) {
cerr << wrd(e) << WY_ENDL;
return -1; // e.c_errno();
}
catch(...) {
cerr << "main() caught(...)" WY_ENDL;
throw;
};
--------------

[]$ g++ a_ptytest.cpp -lwy
[]$ ./a.out
sh-5.2$ hello
hello
sh: hello: command not found
sh-5.2$ sudo ls /root
sudo ls /root
[sudo] password for wij: PasswdIsAccessible

anaconda-ks.cfg
sh-5.2$ exit
exit
exit
OK

What's the significance of C++std library? Probably just demo. of flexibility
and support of AT%T's old software. There is no free lunch.
wij
2024-11-27 05:24:52 UTC
Reply
Permalink
Post by wij
C++ was once? an advanced language for system programming. Now what? As usual, it
wants more, so the answer should be yes it still is.
---- a_ptytest.cpp
/* Copyright is licensed by GNU LGPL, see file COPYING.       by I.J.Wang 2008
  Convert the example program ptytest.c in Linux Application Development
  [Michael K.Johnson, Erik W. Troan] p400. See the book for details.
  See also
  [Advanced Programming in the UNIX(r) Environment 2nd edition, W.R.S, S.A.R.]
  Type 'exit' (or Cntrl-C) in the new pty to exit program.
  Build: make a_ptytest
*/
#include <Wy.stdio.h>
#include <Wy.unistd.h>
#include <Wy.termios.h>
#include <Wy.select.h>
#include <Wy.signal.h>
#include <Wy.pty.h>
using namespace Wy;
void sigwinch_handler(int,::siginfo_t*,void*)
{
};
void t0() {
 Errno r;
 SigAct act(sigwinch_handler,Wy::SigSet(),SA_NOCLDWAIT);
 ByteFlow master;
 ProcessID cpid;
 String name;
 if((r=forkpty(master,cpid,name))!=Ok) {
   WY_THROW(r);
 }
 if(cpid==0) {
   ::execl("/bin/sh", "/bin/sh", NULL);
   ::exit(-1);
 }
 // Parent process
 FdSet rdset,errset;
 rdset.set(cin);
 rdset.set(master);
 errset.set(master);
for(;;) {
   FdSet rset(rdset),eset(errset);
   char buf[512];
   size_t n_rw;
   r=pselect(&rset,NULL,&eset,NULL,NULL);
   if((r!=Ok)&&(r!=EINTR)) {
     cerr << wrd(r) << WY_ENDL;
     break;
   }
   if(eset.contain(master)) {
     WY_THROW( Errno() );
   }
   if(rset.contain(master)) {
     if((r=master.read(buf,WY_CARRLEN(buf),n_rw))!=Ok) {
       // may cause EIO while reading controllong terminal
       if(r!=EIO) {
         WY_THROW(r);
       }
     }
     if(n_rw==0) {
       break;
     }
     if((r=cout.write(buf,n_rw,n_rw))!=Ok) {
       WY_THROW(r);
     }
   }
   if(rset.contain(cin)) {
     String str;
     cin >> str;
     master << str;
   };
 };
};
int main(int argc, const char* argv[])
try {
 t0();
 cout << "OK" WY_ENDL;
 return 0;
}
catch(const Errno& e) {
 cerr << wrd(e) << WY_ENDL;
 return -1;  // e.c_errno();
}
catch(...) {
 cerr << "main() caught(...)" WY_ENDL;
 throw;
};
--------------
[]$ g++ a_ptytest.cpp -lwy
[]$ ./a.out
sh-5.2$ hello
hello
sh: hello: command not found
sh-5.2$ sudo ls /root
sudo ls /root
[sudo] password for wij: PasswdIsAccessible
anaconda-ks.cfg
sh-5.2$ exit
exit
exit
OK
What's the significance of C++std library? Probably just demo. of flexibility
and support of AT%T's old software. There is no free lunch.
Big number arithmetic consumes time. For such tasks, program can also use
a 'run-time library'. Such a program is a valid example:

-------- a_dc.cpp
#include <Wy.stdio.h>
#include <Wy.unistd.h>
#include <Wy.ctype.h>

using namespace Wy;

void process_dc(FifoFile& pipe_rd, FifoFile& pipe_wr) {
Errno r;

if((r=cin.reset(pipe_rd))!=Ok) {
WY_THROW(r);
}
if((r=cout.reset(pipe_wr))!=Ok) {
WY_THROW(r);
}
pipe_rd.reset();
pipe_wr.reset();

::execlp("dc","dc","-",NULL);
};

void process_bc(FifoFile& pipe_rd, FifoFile& pipe_wr) {
Errno r;

for(;;) {
String str;
StrSeg num1,num2;
char op;

cin >> str;
const char* cptr= str.c_str();
for(;isdigit(*cptr); ++cptr) {};

if((r=num1.reset(str.begin(),cptr))!=Ok) {
WY_THROW(r);
}
if(num1.is_empty()) {
cout << "invalid num1" WY_ENDL;
continue;
}

op=0;
switch(*cptr) {
case '+':
case '-':
case '*':
case '/':
case '%':
case '^': op=*cptr;
break;
default: break;
};
if(op==0) {
cout << "invalid op" WY_ENDL;
continue;
}
++cptr;

const char* cptr2=cptr;
for(;isdigit(*cptr2); ++cptr2) {};

if((r=num2.reset(cptr,cptr2))!=Ok) {
WY_THROW(r);
}
if(num2.is_empty()) {
cout << "invalid num2" WY_ENDL;
continue;
}

pipe_wr << num1 << '\n' << num2 << '\n' << op << '\n' << "p\n";
pipe_rd >> str;
cout << str;
}
};

int main(int argc, const char* argv[])
try {
Errno r;
FifoFile pipe_dc_rd, pipe_dc_wr;
FifoFile pipe_bc_rd, pipe_bc_wr;
ProcessID cpid;

cout << "Simple dc calculator" WY_ENDL;

if((r=mkpipe(pipe_dc_rd, pipe_bc_wr))!=Ok) {
WY_THROW(r);
}
if((r=mkpipe(pipe_bc_rd, pipe_dc_wr))!=Ok) {
WY_THROW(r);
}
if((r=fork(cpid))!=Ok) {
WY_THROW(r);
}

if(cpid==0) {
pipe_bc_rd.reset();
pipe_bc_wr.reset();
process_dc(pipe_dc_rd,pipe_dc_wr);
WY_THROW( Errno() );
}

pipe_dc_rd.reset();
pipe_dc_wr.reset();
process_bc(pipe_bc_rd,pipe_bc_wr);
cout << "OK" WY_ENDL;
return 0;
}
catch(const Errno& e) {
cerr << wrd(e) << WY_ENDL;
return -1; // e.c_errno();
}
catch(...) {
cerr << "main() caught(...)" WY_ENDL;
throw;
};
---------------

[]$ g++ a_dc.cpp -lwy
[]$ ./a.out
Simple dc calculator
1+2
3
3^100
515377520732011331036461129765621272702107522001
^C

Since this is comp.lang.c++, what would a program using c++std library look like?
wij
2024-11-29 06:52:13 UTC
Reply
Permalink
Post by wij
int main(int argc, const char* argv[])
try {
 Errno r;
 FifoFile pipe_dc_rd, pipe_dc_wr;
 FifoFile pipe_bc_rd, pipe_bc_wr;
 ProcessID cpid;
 cout << "Simple dc calculator" WY_ENDL;
 if((r=mkpipe(pipe_dc_rd, pipe_bc_wr))!=Ok) {
   WY_THROW(r);
 }
 if((r=mkpipe(pipe_bc_rd, pipe_dc_wr))!=Ok) {
   WY_THROW(r);
 }
 if((r=fork(cpid))!=Ok) {
   WY_THROW(r);
 }
 if(cpid==0) {
   pipe_bc_rd.reset();
   pipe_bc_wr.reset();
   process_dc(pipe_dc_rd,pipe_dc_wr);
   WY_THROW( Errno() );
 }
 pipe_dc_rd.reset();
 pipe_dc_wr.reset();
 process_bc(pipe_bc_rd,pipe_bc_wr);
 cout << "OK" WY_ENDL;
 return 0;
}
catch(const Errno& e) {
 cerr << wrd(e) << WY_ENDL;
 return -1;  // e.c_errno();
}
catch(...) {
 cerr << "main() caught(...)" WY_ENDL;
 throw;
};
---------------
This is routine codes, so made it a function (popen):

Errno popen(ProcessID& cpid, FifoFile& rdf, FifoFile& wrf, const char* fpath,
char* const argv[], char* const envp[]) {
Errno r;
FifoFile pipe0_rd, pipe0_wr;
FifoFile pipe1_rd, pipe1_wr;

if((r=mkpipe(pipe1_rd, pipe0_wr))!=Ok) { // fd FD_CLOEXEC ?
WY_RETURN(r);
}
if((r=mkpipe(pipe0_rd, pipe1_wr))!=Ok) {
WY_RETURN(r);
}
if((r=fork(cpid))!=Ok) {
WY_RETURN(r);
}

if(cpid==0) {
ByteFlow nulf("/dev/null",O_WRONLY);
pipe1_rd.reset();
pipe1_wr.reset();
if((r=cin.reset(pipe0_rd))!=Ok) {
WY_THROW(r); // go where ???
}
if((r=cout.reset(pipe0_wr))!=Ok) {
WY_THROW(r);
}
if((r=cerr.reset(nulf))!=Ok) {
WY_THROW(r);
}
pipe0_rd.reset();
pipe0_wr.reset();

if(envp==NULL) {
if(::execvp(fpath,argv)==-1) {
WY_THROW( Errno(errno) );
}
} else {
if(::execve(fpath,argv,envp)==-1) {
WY_THROW( Errno(errno) );
}
}

WY__UNREACHABLE__;
}

pipe0_rd.reset();
pipe0_wr.reset();
if((r=rdf.reset(pipe1_rd))!=Ok) {
WY_RETURN(r);
}
if((r=wrf.reset(pipe1_wr))!=Ok) {
WY_RETURN(r);
}
pipe1_rd.reset();
pipe1_wr.reset();

return Ok;
};
----------

Q1. size of argv[]
As I know, no way to know the number of elements in argv[]

Q2. A measure of a wrapper library: Is it still efficient for implementing
popen or other wrapper functions in it own library?

For the popen above:
wrt compiled size: should be fine (real difference is minor).
wrt speed: a number of protocol adjustment codes should be fine (mostly 
are about error handling).
wrt c++std library: I don't think it can implement popen using its own
library facilities. This is generally true, particularly std::vector
has nothing to do with malloc(3).

Q3. What the exit code of a program should be?
This may be an example exit code is better errno (or compatible)
geodandw
2024-12-04 03:38:08 UTC
Reply
Permalink
On 11/29/24 01:52, wij wrote:
---
Post by wij
Q1. size of argv[]
As I know, no way to know the number of elements in argv[]
---
For number of args in argv, see argc.

Loading...