getch not working without initscr - ncurses

I gone through the Documentation of NCURSES. I am not getting that if I use getch without initscr then why this program is not working. Is there any other approach to get arrow keys input without clearing screen (that initscr do).
#include <ncurses.h>
#include <unistd.h>
int main()
{
int ch;
//initscr();
//raw();
//keypad(stdscr, TRUE);
//noecho();
while(1)
{
ch = getch();
switch(ch)
{
case KEY_UP:
printw("\nUp Arrow");
break;
case KEY_DOWN:
printw("\nDown Arrow");
break;
case KEY_LEFT:
printw("\nLeft Arrow");
break;
case KEY_RIGHT:
printw("\nRight Arrow");
break;
}
if(ch == KEY_UP)
break;
}
//endwin();
}

Alternatively you may use change the terminal attribute through tcsetattr in termios. If you cycle between the canonical mode (requires new line for the process to begin) and non-canonocal mode (Keypress is more than enough).
The following program works as follows - THe process waits for user input. If up arrow key is pressed, it prints 'Arrow key pressed' and exits. If something else is pressed, it waits for the user to press Enter and then prints the user inpu. Exits after the inut is printed.
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
struct termios oldt, newt;
char ch, command[20];
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
while(1)
{
ch = getchar();
if (ch == '\033')
{ printf("Arrow key\n"); ch=-1; break;}
else if(ch == -1) // by default the function returns -1, as it is non blocking
{
continue;
}
else
{
break;
}
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch,stdin);ith
putchar(ch);
scanf("%s",command);
printf("\n%s\n",command);
return 1;
}
return 0;
}

getch is the same as wgetch(stdscr). It assumes that the screen has been initialized. ncurses (any curses implementation) has to do a couple of things to make wgetch usable:
read the terminal description
initialize the terminal I/O modes
allocate a screen to draw on.
The last is because wgetch does a wrefresh on the window for which it was called, before doing a read.
You could use newterm with filter to avoid clearing the screen, and doing line-oriented input. The filter program in ncurses-examples demonstrates how to do this.

I have a solution without ncurses
You can use simple-getch like this:
t_key keys[] = {
{"[A", K_UP},
{"[B", K_DOWN},
{"[D", K_LEFT},
{"[C", K_RIGHT},
{NULL, K_UNK},
};
int key;
ch_init();
while ((key = ch_get(keys)) != K_BACK)
{
printf("%d\n", key);
}
ch_end();
keys array is a list of escape sequences which will be used, (when you type an arrow key in a terminal, it will write an escape key followed by multiples characters to define the key.)
This sequences may/will change between terminals, you should use termcap to properly set this sequences.

Related

How to use use XNextEvent, XPeekEvent and XPutBackEvent

I am trying to develop a little Linux application to perform measurements directly on the screen i.e. to measure distances and angles in images, documents etc.
To this end I was inspired by How to listen for mouse events in Linux?
and thought I could use XNextEvent or XPeekEvent and possibly XPutBackEvent.
This program responds nicely to mouse events but since it uses XNextEvent the bucket stops there and the event is not sent to the target window and the mouse becomes useless and if I use XPutBackEvent the same event comes back again.
To fix this I replaced XNextEvent with XPeekEvent, which is supposed to send the event up the ladder. This didn't help but made things worse sending an endless stream of the same event, filtering
out equal events didn't help either.
Thanks in advance for any tip to solve this problem.
The problem is basically that the events do not disappear and I don't know how to get rid of them in order to be able to use the mouse. The code below is copied from
How to listen for mouse events in Linux? and somewhat modified.
#include <stdio.h>
#include <X11/Xlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char *key_name[] = {
"first",
"second (or middle)",
"third",
"fourth", // :D
"fivth" // :|
};
int main(int argc, char **argv)
{
Display *display;
XEvent xevent,oldevent;
Window window;
long unsigned int buttontime=0;
if( (display = XOpenDisplay(NULL)) == NULL )
return -1;
window = DefaultRootWindow(display);
XAllowEvents(display, AsyncBoth, CurrentTime);
XGrabPointer(display,
window,
1,
//PointerMotionMask |
ButtonPressMask | ButtonReleaseMask ,
GrabModeAsync,
GrabModeAsync,
None,
None,
CurrentTime);
while(1)
{
XNextEvent(display, &xevent);
//XPeekEvent(display, &xevent);
switch (xevent.type) {
/*case MotionNotify:
printf("Mouse move : [%d, %d]\n", xevent.xmotion.x_root, xevent.xmotion.y_root);
break;*/
case ButtonPress:
//if ((buttontime <= xevent.xbutton.time))
{
printf("Button pressed : %s\n", key_name[xevent.xbutton.button - 1]);
printf("timevent %lu\n",xevent.xbutton.time);
printf("timeold %lu\n",buttontime);
buttontime = xevent.xbutton.time;
buttontime++;
}
break;
case ButtonRelease:
printf("Button released : %s\n", key_name[xevent.xbutton.button - 1]);
break;
}
// XPutBackEvent(display, &xevent);
sleep(1);
}
return 0;
}

Automatic subdir and file creation in C [duplicate]

I want an easy way to create multiple directories in C++/Linux.
For example I want to save a file lola.file in the directory:
/tmp/a/b/c
but if the directories are not there I want them to be created automagically. A working example would be perfect.
Easy with Boost.Filesystem: create_directories
#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");
Returns: true if a new directory was created, otherwise false.
With C++17 or later, there's the standard header <filesystem> with
function
std::filesystem::create_directories
which should be used in modern C++ programs.
The C++ standard functions do not have the POSIX-specific explicit
permissions (mode) argument, though.
However, here's a C function that can be compiled with C++ compilers.
/*
#(#)File: mkpath.c
#(#)Purpose: Create all directories in path
#(#)Author: J Leffler
#(#)Copyright: (C) JLSS 1990-2020
#(#)Derivation: mkpath.c 1.16 2020/06/19 15:08:10
*/
/*TABSTOP=4*/
#include "posixver.h"
#include "mkpath.h"
#include "emalloc.h"
#include <errno.h>
#include <string.h>
/* "sysstat.h" == <sys/stat.h> with fixup for (old) Windows - inc mode_t */
#include "sysstat.h"
typedef struct stat Stat;
static int do_mkdir(const char *path, mode_t mode)
{
Stat st;
int status = 0;
if (stat(path, &st) != 0)
{
/* Directory does not exist. EEXIST for race condition */
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
}
else if (!S_ISDIR(st.st_mode))
{
errno = ENOTDIR;
status = -1;
}
return(status);
}
/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
char *pp;
char *sp;
int status;
char *copypath = STRDUP(path);
status = 0;
pp = copypath;
while (status == 0 && (sp = strchr(pp, '/')) != 0)
{
if (sp != pp)
{
/* Neither root nor double slash in path */
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if (status == 0)
status = do_mkdir(path, mode);
FREE(copypath);
return (status);
}
#ifdef TEST
#include <stdio.h>
#include <unistd.h>
/*
** Stress test with parallel running of mkpath() function.
** Before the EEXIST test, code would fail.
** With the EEXIST test, code does not fail.
**
** Test shell script
** PREFIX=mkpath.$$
** NAME=./$PREFIX/sa/32/ad/13/23/13/12/13/sd/ds/ww/qq/ss/dd/zz/xx/dd/rr/ff/ff/ss/ss/ss/ss/ss/ss/ss/ss
** : ${MKPATH:=mkpath}
** ./$MKPATH $NAME &
** [...repeat a dozen times or so...]
** ./$MKPATH $NAME &
** wait
** rm -fr ./$PREFIX/
*/
int main(int argc, char **argv)
{
int i;
for (i = 1; i < argc; i++)
{
for (int j = 0; j < 20; j++)
{
if (fork() == 0)
{
int rc = mkpath(argv[i], 0777);
if (rc != 0)
fprintf(stderr, "%d: failed to create (%d: %s): %s\n",
(int)getpid(), errno, strerror(errno), argv[i]);
exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
}
int status;
int fail = 0;
while (wait(&status) != -1)
{
if (WEXITSTATUS(status) != 0)
fail = 1;
}
if (fail == 0)
printf("created: %s\n", argv[i]);
}
return(0);
}
#endif /* TEST */
The macros STRDUP() and FREE() are error-checking versions of
strdup() and free(), declared in emalloc.h (and implemented in
emalloc.c and estrdup.c).
The "sysstat.h" header deals with broken versions of <sys/stat.h>
and can be replaced by <sys/stat.h> on modern Unix systems (but there
were many issues back in 1990).
And "mkpath.h" declares mkpath().
The change between v1.12 (original version of the answer) and v1.13
(amended version of the answer) was the test for EEXIST in
do_mkdir().
This was pointed out as necessary by
Switch — thank
you, Switch.
The test code has been upgraded and reproduced the problem on a MacBook
Pro (2.3GHz Intel Core i7, running Mac OS X 10.7.4), and suggests that
the problem is fixed in the revision (but testing can only show the
presence of bugs, never their absence).
The code shown is now v1.16; there have been cosmetic or administrative
changes made since v1.13 (such as use mkpath.h instead of jlss.h and
include <unistd.h> unconditionally in the test code only).
It's reasonable to argue that "sysstat.h" should be replaced by
<sys/stat.h> unless you have an unusually recalcitrant system.
(You are hereby given permission to use this code for any purpose with attribution.)
This code is available in my SOQ
(Stack Overflow Questions) repository on GitHub as files mkpath.c and
mkpath.h (etc.) in the
src/so-0067-5039
sub-directory.
system("mkdir -p /tmp/a/b/c")
is the shortest way I can think of (in terms of the length of code, not necessarily execution time).
It's not cross-platform but will work under Linux.
Here is my example of code (it works for both Windows and Linux):
#include <iostream>
#include <string>
#include <sys/stat.h> // stat
#include <errno.h> // errno, ENOENT, EEXIST
#if defined(_WIN32)
#include <direct.h> // _mkdir
#endif
bool isDirExist(const std::string& path)
{
#if defined(_WIN32)
struct _stat info;
if (_stat(path.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & _S_IFDIR) != 0;
#else
struct stat info;
if (stat(path.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & S_IFDIR) != 0;
#endif
}
bool makePath(const std::string& path)
{
#if defined(_WIN32)
int ret = _mkdir(path.c_str());
#else
mode_t mode = 0755;
int ret = mkdir(path.c_str(), mode);
#endif
if (ret == 0)
return true;
switch (errno)
{
case ENOENT:
// parent didn't exist, try to create it
{
int pos = path.find_last_of('/');
if (pos == std::string::npos)
#if defined(_WIN32)
pos = path.find_last_of('\\');
if (pos == std::string::npos)
#endif
return false;
if (!makePath( path.substr(0, pos) ))
return false;
}
// now, try to create again
#if defined(_WIN32)
return 0 == _mkdir(path.c_str());
#else
return 0 == mkdir(path.c_str(), mode);
#endif
case EEXIST:
// done!
return isDirExist(path);
default:
return false;
}
}
int main(int argc, char* ARGV[])
{
for (int i=1; i<argc; i++)
{
std::cout << "creating " << ARGV[i] << " ... " << (makePath(ARGV[i]) ? "OK" : "failed") << std::endl;
}
return 0;
}
Usage:
$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
#include <sys/types.h>
#include <sys/stat.h>
int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
From here. You may have to do separate mkdirs for /tmp, /tmp/a, /tmp/a/b/ and then /tmp/a/b/c because there isn't an equivalent of the -p flag in the C api. Be sure and ignore the EEXISTS errno while you're doing the upper level ones.
It should be noted that starting from C++17 filesystem interface is part of the standard library. This means that one can have following to create directories:
#include <filesystem>
std::filesystem::create_directories("/a/b/c/d")
More info here: https://en.cppreference.com/w/cpp/filesystem/create_directory
Additionally, with gcc, one needs to "-std=c++17" to CFLAGS. And "-lstdc++fs" to LDLIBS. The latter potentially is not going to be required in the future.
This is similar to the previous but works forward through the string instead of recursively backwards. Leaves errno with the right value for last failure. If there's a leading slash, there's an extra time through the loop which could have been avoided via one find_first_of() outside the loop or by detecting the leading / and setting pre to 1. The efficiency is the same whether we get set up by a first loop or a pre loop call, and the complexity would be (slightly) higher when using the pre-loop call.
#include <iostream>
#include <string>
#include <sys/stat.h>
int
mkpath(std::string s,mode_t mode)
{
size_t pos=0;
std::string dir;
int mdret;
if(s[s.size()-1]!='/'){
// force trailing / so we can handle everything in loop
s+='/';
}
while((pos=s.find_first_of('/',pos))!=std::string::npos){
dir=s.substr(0,pos++);
if(dir.size()==0) continue; // if leading / first time is 0 length
if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){
return mdret;
}
}
return mdret;
}
int main()
{
int mkdirretval;
mkdirretval=mkpath("./foo/bar",0755);
std::cout << mkdirretval << '\n';
}
You said "C++" but everyone here seems to be thinking "Bash shell."
Check out the source code to gnu mkdir; then you can see how to implement the shell commands in C++.
bool mkpath( std::string path )
{
bool bSuccess = false;
int nRC = ::mkdir( path.c_str(), 0775 );
if( nRC == -1 )
{
switch( errno )
{
case ENOENT:
//parent didn't exist, try to create it
if( mkpath( path.substr(0, path.find_last_of('/')) ) )
//Now, try to create again.
bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
else
bSuccess = false;
break;
case EEXIST:
//Done!
bSuccess = true;
break;
default:
bSuccess = false;
break;
}
}
else
bSuccess = true;
return bSuccess;
}
So I need mkdirp() today, and found the solutions on this page overly complicated.
Hence I wrote a fairly short snippet, that easily be copied in for others who
stumble upon this thread an wonder why we need so many lines of code.
mkdirp.h
#ifndef MKDIRP_H
#define MKDIRP_H
#include <sys/stat.h>
#define DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
/** Utility function to create directory tree */
bool mkdirp(const char* path, mode_t mode = DEFAULT_MODE);
#endif // MKDIRP_H
mkdirp.cpp
#include <errno.h>
bool mkdirp(const char* path, mode_t mode) {
// const cast for hack
char* p = const_cast<char*>(path);
// Do mkdir for each slash until end of string or error
while (*p != '\0') {
// Skip first character
p++;
// Find first slash or end
while(*p != '\0' && *p != '/') p++;
// Remember value from p
char v = *p;
// Write end of string at p
*p = '\0';
// Create folder from path to '\0' inserted at p
if(mkdir(path, mode) == -1 && errno != EEXIST) {
*p = v;
return false;
}
// Restore path to it's former glory
*p = v;
}
return true;
}
If you don't like const casting and temporarily modifying the string, just do a strdup() and free() it afterwards.
Since this post is ranking high in Google for "Create Directory Tree", I am going to post an answer that will work for Windows — this will work using Win32 API compiled for UNICODE or MBCS. This is ported from Mark's code above.
Since this is Windows we are working with, directory separators are BACK-slashes, not forward slashes. If you would rather have forward slashes, change '\\' to '/'
It will work with:
c:\foo\bar\hello\world
and
c:\foo\bar\hellp\world\
(i.e.: does not need trailing slash, so you don't have to check for it.)
Before saying "Just use SHCreateDirectoryEx() in Windows", note that SHCreateDirectoryEx() is deprecated and could be removed at any time from future versions of Windows.
bool CreateDirectoryTree(LPCTSTR szPathTree, LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL){
bool bSuccess = false;
const BOOL bCD = CreateDirectory(szPathTree, lpSecurityAttributes);
DWORD dwLastError = 0;
if(!bCD){
dwLastError = GetLastError();
}else{
return true;
}
switch(dwLastError){
case ERROR_ALREADY_EXISTS:
bSuccess = true;
break;
case ERROR_PATH_NOT_FOUND:
{
TCHAR szPrev[MAX_PATH] = {0};
LPCTSTR szLast = _tcsrchr(szPathTree,'\\');
_tcsnccpy(szPrev,szPathTree,(int)(szLast-szPathTree));
if(CreateDirectoryTree(szPrev,lpSecurityAttributes)){
bSuccess = CreateDirectory(szPathTree,lpSecurityAttributes)!=0;
if(!bSuccess){
bSuccess = (GetLastError()==ERROR_ALREADY_EXISTS);
}
}else{
bSuccess = false;
}
}
break;
default:
bSuccess = false;
break;
}
return bSuccess;
}
I know it's an old question but it shows up high on google search results and the answers provided here are not really in C++ or are a bit too complicated.
Please note that in my example createDirTree() is very simple because all the heavy lifting (error checking, path validation) needs to be done by createDir() anyway. Also createDir() should return true if directory already exists or the whole thing won't work.
Here's how I would do that in C++:
#include <iostream>
#include <string>
bool createDir(const std::string dir)
{
std::cout << "Make sure dir is a valid path, it does not exist and create it: "
<< dir << std::endl;
return true;
}
bool createDirTree(const std::string full_path)
{
size_t pos = 0;
bool ret_val = true;
while(ret_val == true && pos != std::string::npos)
{
pos = full_path.find('/', pos + 1);
ret_val = createDir(full_path.substr(0, pos));
}
return ret_val;
}
int main()
{
createDirTree("/tmp/a/b/c");
return 0;
}
Of course createDir() function will be system-specific and there are already enough examples in other answers how to write it for linux, so I decided to skip it.
So many approaches has been described here but most of them need hard coding of your path into your code.
There is an easy solution for that problem, using QDir and QFileInfo, two classes of Qt framework. Since your already in Linux environment it should be easy to use Qt.
QString qStringFileName("path/to/the/file/that/dont/exist.txt");
QDir dir = QFileInfo(qStringFileName).dir();
if(!dir.exists()) {
dir.mkpath(dir.path());
}
Make sure you have write access to that Path.
If dir does not exist, create it:
boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str());
Here's C/C++ recursive function that makes use of dirname() to traverse bottom-up the directory tree. It will stop as soon as it finds an existing ancestor.
#include <libgen.h>
#include <string.h>
int create_dir_tree_recursive(const char *path, const mode_t mode)
{
if (strcmp(path, "/") == 0) // No need of checking if we are at root.
return 0;
// Check whether this dir exists or not.
struct stat st;
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode))
{
// Check and create parent dir tree first.
char *path2 = strdup(path);
char *parent_dir_path = dirname(path2);
if (create_dir_tree_recursive(parent_dir_path, mode) == -1)
return -1;
// Create this dir.
if (mkdir(path, mode) == -1)
return -1;
}
return 0;
}
mkdir -p /dir/to/the/file
touch /dir/to/the/file/thefile.ending
If you don't have C++17 yet and look for a platform agnostic solution, use ghc::filesystem. The header-ony code is compatible to C++17 (in fact a backport) and easy to migrate later on.
The others got you the right answer, but I thought I'd demonstrate another neat thing you can do:
mkdir -p /tmp/a/{b,c}/d
Will create the following paths:
/tmp/a/b/d
/tmp/a/c/d
The braces allow you to create multiple directories at once on the same level of the hierarchy, whereas the -p option means "create parent directories as needed".

forkpty works for some terminal apps, not for others

I'm trying to write a pty I/O transparent filter for a shell.
The following example mostly works. Most programs run as expected with the wrapper. This example does not do any filtering, it's purpose is just to provide a framework.
EDIT: With my answer below I got this example working. I've updated the example here to reflect that.
Here is the now working code:
/*
This example is public domain. Use as you see fit.
The purpose of this example is show how a process can run a shell transparently and be able to filter it's input and output.
This example does not show off any I/O filtering, it only provides the framework on which that could be added.
Tested only on GNU/Linux with recent kernels and recent g++ and clang++
This example is based on original found here:
https://www.scriptjunkie.us/wp-content/uploads/2011/04/stdioterminallogger.c
There were 2 problems with the code this example was based on.
1) Terminal (re)sizing was not being handled
2) Some applications display incorrectly or keys don't work
a) with 'joe', Enter, Ctrl-M, and Ctrl-J don't work
b) 'joe' has display isues
c) 'snake' (game) has display issues
Also, be aware of this:
#define LOGFILELOCATION "/tmp/.shlog"
in the original code, not this example.
This example does not write produce any files. (intermediate or otherwise)
The following programs do seem to work correctly:
vi, vim, nano, mcedit, htop, top
#1 has been solved with a resize handler (see handler and handleTerminalResize)
Use this in shell's profile/bashrc to indicate pty-filter is present
[ "${inptyfilter}" == "true" ] && PS1="(pty-filter) ${PS1}"
Compile with any of the following:
g++ -std=c++11 pty-filter.cpp -lutil -o pty-filter
g++ -std=c++1y pty-filter.cpp -lutil -o pty-filter
g++ -std=c++1z pty-filter.cpp -lutil -o pty-filter
clang++ -std=c++11 pty-filter.cpp -lutil -o pty-filter
clang++ -std=c++1y pty-filter.cpp -lutil -o pty-filter
clang++ -std=c++1z pty-filter.cpp -lutil -o pty-filter
# for stricter compilation:
clang++ -std=c++1z pty-filter.cpp -lutil -o pty-filter -Wall -Werror -Weverything -Wno-c++98-compat -Wno-missing-prototypes -Wno-disabled-macro-expansion -Wno-vla-extension -Wno-vla
*/
// standard C stuff
#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <cerrno>
#include <cstdarg>
// C++ stuff
#include <string>
// Everything else
#include <pty.h>
#include <unistd.h>
#include <termios.h>
#include <sys/mman.h>
#include <sys/wait.h>
// shared globals
struct sharedBookT {
pid_t childPid;
pid_t parentPid;
pid_t shellPid;
int shellFd;
termios oldTerm, newTerm, shellTerm;
bool readyToQuit;
char fromTerminalBuffer [4096];
char toTerminalBuffer [4096];
char padding [3];
};
// avoid non C++ casts (when used with stricter compilation)
typedef const char* constCharPtrT;
typedef void* voidPtr;
typedef sharedBookT* sharedBookPtrT;
static sharedBookPtrT sharedBookPtr = 0;
// sprintf for std::string
std::string Sprintf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
std::string Sprintf (const char* fmt, ...) {
va_list ap;
va_start (ap, fmt);
const auto n = vsnprintf (0, 0, fmt, ap);
va_end (ap);
char result [n+2];
va_start (ap, fmt);
vsnprintf (result, size_t (n+1), fmt, ap);
va_end (ap);
return std::string (result);
}
// c_str and length shortcut operators for std::string
const char* operator* (const std::string& s) { return s.c_str (); }
size_t operator+ (const std::string& s) { return s.length (); }
// resize shell's pty and notifiy chell of change
void handleTerminalResize () {
sharedBookT& shared = *sharedBookPtr;
winsize ws;
ioctl(0, TIOCGWINSZ, &ws);
ioctl(shared.shellFd, TIOCSWINSZ, &ws);
sigqueue (shared.shellPid, SIGWINCH, {0});
}
// log signal, for convience just to stdout
void logsignal (int signal) {
// can't reliably use regular printf from a signal handler
const auto msg = Sprintf ("Got signal %d\n", signal);
write (1, *msg, +msg);
}
// common signal handler
void handler(int signal, siginfo_t * infoP, void *context __attribute__ ((unused))) {
const auto& si = *infoP;
const auto myPid = getpid ();
sharedBookT& shared = *sharedBookPtr;
// using SIGUSR to notify processes of termination
// (processes must check for it after blocking syscalls)
if (signal == SIGUSR2) { // Notification to quit
shared.readyToQuit = true;
return;
}
auto cc = char (-1);
if (myPid == shared.parentPid) {
// only parent process should handle these
// if child processes handle these as well, there are multiple insertions
switch (si.si_signo) {
case SIGINT: cc = 0x03; break; // "Ctrl-C"
case SIGTSTP: cc = 0x1A; break; // "Ctrl-Z"
case SIGQUIT: cc = 0x1C; break; // "Ctrl-\"
case SIGWINCH: handleTerminalResize (); break;
default: logsignal (signal); break;
}
}
// write control character (if any) to shell's pty
if (-1 < cc) write(shared.shellFd, &cc, 1);
}
// Add common signal handler for each signal
void setupsignal(int signal) {
struct sigaction act;
sigaction(signal, NULL, &act);
act.sa_sigaction = handler;
act.sa_flags |= SA_SIGINFO;
sigaction(signal, &act, NULL);
}
// launch shell with new pty
void launchShell () {
sharedBookT& shared = *sharedBookPtr;
tcgetattr(0, &shared.shellTerm);
const auto pid = forkpty(&shared.shellFd, NULL, &shared.shellTerm, NULL);
if (pid == -1 || pid == 0) {
if (pid == 0) {
shared.shellPid = getpid ();
// inform shell it's pty is being filtered
setenv ("inptyfilter", "true", 1);
exit(execlp("/bin/bash", "bash", NULL));
}
else {
perror ("forkpty failed");
exit (1);
}
}
}
int main () {
// create shared globals structure
sharedBookPtr = sharedBookPtrT (mmap (
NULL, sizeof (sharedBookT),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0
));
sharedBookT& shared = *sharedBookPtr;
launchShell ();
shared.parentPid = getpid ();
//Set up handler for signals
setupsignal(SIGINT);
setupsignal(SIGTSTP);
setupsignal(SIGUSR1);
setupsignal(SIGUSR2);
setupsignal(SIGQUIT);
setupsignal(SIGWINCH);
//setupsignal(SIGTTIN);
//setupsignal(SIGTTOU);
// fork to handle output to the terminal
if (0 == fork ()) {
shared.childPid = getpid ();
// loop while reading and echoing the pty's output
for (;;) {
// read from Shell's Pty
const auto charsRead = read (shared.shellFd, shared.toTerminalBuffer, sizeof (shared.toTerminalBuffer));
// if characters were read, echo them and continue
if (0 < charsRead) {
write (1, shared.toTerminalBuffer, size_t (charsRead));
continue;
}
// if error, check if we are done
if ((charsRead == -1) and (errno == EIO)) {
fprintf (stderr, "\nterminating I/O processes\r\n");
// signal parent to exit
sigqueue (shared.parentPid, SIGUSR2, {0});
break;
}
}
fprintf (stderr, "Exiting pty-filter (toTerminal)\r\n");
exit (0);
}
// wait for pids to be updated
while ((0 == shared.shellPid) or (0 == shared.childPid)) usleep (1);
fprintf (stderr, "parent: %d\n", shared.parentPid);
fprintf (stderr, "shell: %d\n", shared.shellPid);
fprintf (stderr, "child: %d\n", shared.childPid);
tcgetattr(0, &shared.oldTerm); // Disable buffered I/O and echo mode for pty
shared.newTerm = shared.oldTerm;
cfmakeraw (&shared.newTerm);
tcsetattr(0, TCSANOW, &shared.newTerm);
// shell needs intial sizing
handleTerminalResize ();
for (;;) {//loop while processing input from pty
const auto charsRead = read (0, shared.fromTerminalBuffer, sizeof (shared.fromTerminalBuffer));
// SIGUSR1 will drop process out of read so flag can be read
if (shared.readyToQuit) {
fprintf (stderr, "Exiting pty-filter (fromTerminal)\r\n");
break;
}
// in we got input from the terminal, pass it on to the shell's pty
if (0 < charsRead) {
write (shared.shellFd, shared.fromTerminalBuffer, size_t (charsRead));
continue;
}
// if error check if we are done
// However, this is never executed, child fork terminates first
if ((charsRead == -1) and (errno == EIO)) break;
}
tcsetattr(0, TCSANOW, &shared.oldTerm); //reset terminal
// wait for child forks to exit
for (;;) {
auto wpid = wait (0);
if (wpid == -1) break;
fprintf (stderr, "%d is done\n", wpid);
}
perror ("status");
return 0;
}
My question is, what am I missing? What would cause some programs (like joe and snake) to display erratically, while many other programs (like vi, vim, nano, mcedit, htop, top) seem to work just fine.
(On my system joe and snake work just fine without the "pty filter".)
EDIT: As stated above, it now works
Replacing this:
shared.newTerm.c_lflag &= tcflag_t (~ICANON);
shared.newTerm.c_lflag &= tcflag_t (~ECHO);
with this:
shared.newTerm.c_lflag &= tcflag_t (~(ICANON | ISIG | IEXTEN | ECHO));
shared.newTerm.c_iflag &= tcflag_t (~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | INPCK | ISTRIP | IXON | PARMRK));
shared.newTerm.c_oflag &= tcflag_t (~OPOST);
shared.newTerm.c_cc[VMIN] = 1; // 1 char at a time input
shared.newTerm.c_cc[VTIME] = 0;
made it start working correctly. However, this seems like it should not have any effect, as this is being done on stdin:
shared.newTerm.c_oflag &= tcflag_t (~OPOST);
EDIT: The following post answers the question about stdin vs stdout for tcsetattr.
When setting terminal attributes via tcsetattr(fd.....), can fd be either stdout or stdin?
But anyways, it works now. I will update my original post to reflect this.
EDIT: This post was marked as related: Using the linux pseudo terminal API for multiple debug terminals
While the answer was not on that post, it pointed to a site that had the information I needed: http://www.man7.org/tlpi/code/online/dist/tty/tty_functions.c.html
EDIT: Replacing the above with the following also works. I will update my original post accordingly.:
cfmakeraw (&shared.newTerm);

Linux Pipes and Redirecting output

I'm trying to write a linux program using pipes, but so far I've encountered a major problem.
When I try to run this, it seems it either duplicates the answers, or doesnt give me an answer at all.
So far I'm trying to use a pipe, the parent gets a string from the keyboard, and compares it to see if matches any other commands, momentarily its only the "login" command.
But it doesnt work as it doesnt show me a fail or success message. I've been fiddeling with the code, but sometimes it's repeating the answer several time, like it's executing the child several times. Can someone explain me why its happening?
Thx
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int fd[2], nbytes;
pid_t childpid;
char input[12];
char readbuffer[80];
int log_variable;
int pid;
int compare(char str1[], char str2[]){
if(strlen(str1) == strlen(str2))
{int i;
for( i=0; i<strlen(str1); i++){
if(str1[i] != str2[i])
return 0;
return 1;
}
}
}
int test(char argument[]){//test function
pipe(fd);
switch(childpid=fork()){
case -1:
perror("fork -1\n");
exit(1);
case 0://child
close (fd[1]);
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
if(compare(readbuffer, "login") == 1){
return 1;
}
else if(compare(readbuffer, "login") == 0){
return 0;
}
exit(1);
default:
//parent
close(fd[0]);
write(fd[1], argument, sizeof(argument));
while(wait(NULL)!=-1);
}
}
main(){
while(1){
printf("Insert command: \n");
scanf("%s", input);
logs=(test(input));
if(logs == 1) {printf("success\n"); break;}
else if(logs == 0) {printf("fail\n"); continue;}
}
return 0;
}
a couple of problems for your code with a quick look:
compare function doesn't return value if length not equal.
test() function may get called twice in one process, which means fork more times.
the test() internally for the child will return to the main, also parent will return to main ... get things more complicated here (the child may fork a third time ...)
Use "strace -F" can give you a much better view what things happened behind.

Cannot generate keystroke written to device kernel with input.h Linux

I am trying to generate keystrokes and write them to the kernel in Linux using the input library. I found an example form http://rico-studio.com/linux/read-and-write-to-a-keyboard-device/ and made a little test program. It should just print a bunch of t's but only does this when I strike a key (space for example) myself.
#include <linux/input.h>
#include <linux/uinput.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define EV_PRESSED 1
#define EV_RELEASED 0
#define EV_REPEAT 2
int main() {
int fd = 0;
/*In my case event3 handles the keyboard. This can be checked typing
* cat /proc/bus/input/devices in the terminal
*/
char *device = "/dev/input/event3";
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
if( (fd = open(device, O_RDWR | O_NONBLOCK )) < 0 )
{
printf("not opened "); // Read or Write to device
return 0;
}
for(int i=0;i <500 ;i++)
{
// usleep(1000);
event.type = EV_KEY;
// Press the key down
event.value = EV_PRESSED;
event.code = KEY_T;
write(fd, &event, sizeof(struct input_event));
// usleep(1000);
// Release the key
event.value = EV_RELEASED;
event.code = KEY_T;
write(fd, &event, sizeof(struct input_event));
usleep(100000);
}
close(fd);
return 0;
}
Maybe this key-stroke flushes the memory along with the generated keystrokes for t written to the devices memory? So I am wondering what I am missing to let it generate a keystroke and write it to the kernel all by itself.
If you run xxd /dev/input3 and break apart the output, you can see that the keyboard is also sending EV_SYN, SYN_REPORT events after each key change to mark the end of a grouped set of events. To do the same:
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(fd, &event, sizeof(struct input_event));

Resources