redirect stdout/stderr to a string - c++

there has been many previous questions about redirecting stdout/stderr to a file. is there a way to redirect stdout/stderr to a string?

Yes, you can redirect it to an std::stringstream:
std::stringstream buffer;
std::streambuf * old = std::cout.rdbuf(buffer.rdbuf());
std::cout << "Bla" << std::endl;
std::string text = buffer.str(); // text will now contain "Bla\n"
You can use a simple guard class to make sure the buffer is always reset:
struct cout_redirect {
cout_redirect( std::streambuf * new_buffer )
: old( std::cout.rdbuf( new_buffer ) )
{ }
~cout_redirect( ) {
std::cout.rdbuf( old );
}
private:
std::streambuf * old;
};

You can use this class:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string>
class StdCapture
{
public:
StdCapture(): m_capturing(false), m_init(false), m_oldStdOut(0), m_oldStdErr(0)
{
m_pipe[READ] = 0;
m_pipe[WRITE] = 0;
if (_pipe(m_pipe, 65536, O_BINARY) == -1)
return;
m_oldStdOut = dup(fileno(stdout));
m_oldStdErr = dup(fileno(stderr));
if (m_oldStdOut == -1 || m_oldStdErr == -1)
return;
m_init = true;
}
~StdCapture()
{
if (m_capturing)
{
EndCapture();
}
if (m_oldStdOut > 0)
close(m_oldStdOut);
if (m_oldStdErr > 0)
close(m_oldStdErr);
if (m_pipe[READ] > 0)
close(m_pipe[READ]);
if (m_pipe[WRITE] > 0)
close(m_pipe[WRITE]);
}
void BeginCapture()
{
if (!m_init)
return;
if (m_capturing)
EndCapture();
fflush(stdout);
fflush(stderr);
dup2(m_pipe[WRITE], fileno(stdout));
dup2(m_pipe[WRITE], fileno(stderr));
m_capturing = true;
}
bool EndCapture()
{
if (!m_init)
return false;
if (!m_capturing)
return false;
fflush(stdout);
fflush(stderr);
dup2(m_oldStdOut, fileno(stdout));
dup2(m_oldStdErr, fileno(stderr));
m_captured.clear();
std::string buf;
const int bufSize = 1024;
buf.resize(bufSize);
int bytesRead = 0;
if (!eof(m_pipe[READ]))
{
bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
}
while(bytesRead == bufSize)
{
m_captured += buf;
bytesRead = 0;
if (!eof(m_pipe[READ]))
{
bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
}
}
if (bytesRead > 0)
{
buf.resize(bytesRead);
m_captured += buf;
}
m_capturing = false;
return true;
}
std::string GetCapture() const
{
std::string::size_type idx = m_captured.find_last_not_of("\r\n");
if (idx == std::string::npos)
{
return m_captured;
}
else
{
return m_captured.substr(0, idx+1);
}
}
private:
enum PIPES { READ, WRITE };
int m_pipe[2];
int m_oldStdOut;
int m_oldStdErr;
bool m_capturing;
bool m_init;
std::string m_captured;
};
call BeginCapture() when you need to start capture
call EndCapture() when you need to stop capture
call GetCapture() to retrieve captured output

In order to provide a thread-safe & cross platform solution, I have adapted rmflow's approach into a similar interface. As this class modifies global file descriptors, I adapted it to a mutex-guarded static class that protects against multiple instances thrashing global file descriptors. In addition, rmflow's answer does not clean up all of the used file descriptors which can lead to problems opening new ones (for output streams or files) if many BeginCapture() & EndCapture() calls are used in one application. This code has been tested on Windows 7/8, Linux, OSX, Android, and iOS.
NOTE: In order to use std::mutex you must compile against c++ 11. If you do not / cannot use c++11, you can remove the mutex calls completely (sacrificing thread safety) or you can find a legacy sychronization mechanism to get the job done.
#ifdef _MSC_VER
#include <io.h>
#define popen _popen
#define pclose _pclose
#define stat _stat
#define dup _dup
#define dup2 _dup2
#define fileno _fileno
#define close _close
#define pipe _pipe
#define read _read
#define eof _eof
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <mutex>
class StdCapture
{
public:
static void Init()
{
// make stdout & stderr streams unbuffered
// so that we don't need to flush the streams
// before capture and after capture
// (fflush can cause a deadlock if the stream is currently being
std::lock_guard<std::mutex> lock(m_mutex);
setvbuf(stdout,NULL,_IONBF,0);
setvbuf(stderr,NULL,_IONBF,0);
}
static void BeginCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_capturing)
return;
secure_pipe(m_pipe);
m_oldStdOut = secure_dup(STD_OUT_FD);
m_oldStdErr = secure_dup(STD_ERR_FD);
secure_dup2(m_pipe[WRITE],STD_OUT_FD);
secure_dup2(m_pipe[WRITE],STD_ERR_FD);
m_capturing = true;
#ifndef _MSC_VER
secure_close(m_pipe[WRITE]);
#endif
}
static bool IsCapturing()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_capturing;
}
static bool EndCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_capturing)
return;
m_captured.clear();
secure_dup2(m_oldStdOut, STD_OUT_FD);
secure_dup2(m_oldStdErr, STD_ERR_FD);
const int bufSize = 1025;
char buf[bufSize];
int bytesRead = 0;
bool fd_blocked(false);
do
{
bytesRead = 0;
fd_blocked = false;
#ifdef _MSC_VER
if (!eof(m_pipe[READ]))
bytesRead = read(m_pipe[READ], buf, bufSize-1);
#else
bytesRead = read(m_pipe[READ], buf, bufSize-1);
#endif
if (bytesRead > 0)
{
buf[bytesRead] = 0;
m_captured += buf;
}
else if (bytesRead < 0)
{
fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
while(fd_blocked || bytesRead == (bufSize-1));
secure_close(m_oldStdOut);
secure_close(m_oldStdErr);
secure_close(m_pipe[READ]);
#ifdef _MSC_VER
secure_close(m_pipe[WRITE]);
#endif
m_capturing = false;
}
static std::string GetCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_captured;
}
private:
enum PIPES { READ, WRITE };
int StdCapture::secure_dup(int src)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup(src);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
return ret;
}
void StdCapture::secure_pipe(int * pipes)
{
int ret = -1;
bool fd_blocked = false;
do
{
#ifdef _MSC_VER
ret = pipe(pipes, 65536, O_BINARY);
#else
ret = pipe(pipes) == -1;
#endif
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
}
void StdCapture::secure_dup2(int src, int dest)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup2(src,dest);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
}
void StdCapture::secure_close(int & fd)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = close(fd);
fd_blocked = (errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
fd = -1;
}
static int m_pipe[2];
static int m_oldStdOut;
static int m_oldStdErr;
static bool m_capturing;
static std::mutex m_mutex;
static std::string m_captured;
};
// actually define vars.
int StdCapture::m_pipe[2];
int StdCapture::m_oldStdOut;
int StdCapture::m_oldStdErr;
bool StdCapture::m_capturing;
std::mutex StdCapture::m_mutex;
std::string StdCapture::m_captured;
call Init() once (before capture) to remove buffering to stdout / stderr
call BeginCapture() when you need to start capture
call EndCapture() when you need to stop capture
call GetCapture() to retrieve captured output
call IsCapturing() to see if stdout/stderr is currently redirected

i've furnished a qt osx ready variation from Björn Pollex code
#include <stdio.h>
#include <iostream>
#include <streambuf>
#include <stdlib.h>
#include <string>
#include <sstream>
class CoutRedirect {
public:
CoutRedirect() {
old = std::cout.rdbuf( buffer.rdbuf() ); // redirect cout to buffer stream
}
std::string getString() {
return buffer.str(); // get string
}
~CoutRedirect( ) {
std::cout.rdbuf( old ); // reverse redirect
}
private:
std::stringstream buffer;
std::streambuf * old;
};

Since your question is tagged C as well as C++, it seems appropriate to mention that although you cannot associate a string to a FILE * in standard C, there are several non-standard libraries that allow that. glibc is almost standard, so you may be perfectly happy using fmemopen() See http://www.gnu.org/s/libc/manual/html_mono/libc.html#String-Streams

I modified class from Sir Digby Chicken Caesar so that it's not static and could be used easily in unit tests. It works for me on Windows compiled by gcc (g++), but I cannot guarantee that it is 100% correct, please leave comments if it is not.
Create object of class StdCapture, and just call BeginCapture() to begin capture and EndCapture() at the end. Code from Init() is moved to the constructor. There shall be only one such object working at a time.
StdCapture.h:
#ifdef _MSC_VER
#include <io.h>
#define popen _popen
#define pclose _pclose
#define stat _stat
#define dup _dup
#define dup2 _dup2
#define fileno _fileno
#define close _close
#define pipe _pipe
#define read _read
#define eof _eof
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <mutex>
#include <chrono>
#include <thread>
#ifndef STD_OUT_FD
#define STD_OUT_FD (fileno(stdout))
#endif
#ifndef STD_ERR_FD
#define STD_ERR_FD (fileno(stderr))
#endif
class StdCapture
{
public:
StdCapture();
void BeginCapture();
bool IsCapturing();
bool EndCapture();
std::string GetCapture();
private:
enum PIPES { READ, WRITE };
int secure_dup(int src);
void secure_pipe(int * pipes);
void secure_dup2(int src, int dest);
void secure_close(int & fd);
int m_pipe[2];
int m_oldStdOut;
int m_oldStdErr;
bool m_capturing;
std::mutex m_mutex;
std::string m_captured;
};
StdCapture.cpp:
#include "StdCapture.h"
StdCapture::StdCapture():
m_capturing(false)
{
// make stdout & stderr streams unbuffered
// so that we don't need to flush the streams
// before capture and after capture
// (fflush can cause a deadlock if the stream is currently being
std::lock_guard<std::mutex> lock(m_mutex);
setvbuf(stdout,NULL,_IONBF,0);
setvbuf(stderr,NULL,_IONBF,0);
}
void StdCapture::BeginCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_capturing)
return;
secure_pipe(m_pipe);
m_oldStdOut = secure_dup(STD_OUT_FD);
m_oldStdErr = secure_dup(STD_ERR_FD);
secure_dup2(m_pipe[WRITE],STD_OUT_FD);
secure_dup2(m_pipe[WRITE],STD_ERR_FD);
m_capturing = true;
#ifndef _MSC_VER
secure_close(m_pipe[WRITE]);
#endif
}
bool StdCapture::IsCapturing()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_capturing;
}
bool StdCapture::EndCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_capturing)
return true;
m_captured.clear();
secure_dup2(m_oldStdOut, STD_OUT_FD);
secure_dup2(m_oldStdErr, STD_ERR_FD);
const int bufSize = 1025;
char buf[bufSize];
int bytesRead = 0;
bool fd_blocked(false);
do
{
bytesRead = 0;
fd_blocked = false;
#ifdef _MSC_VER
if (!eof(m_pipe[READ]))
bytesRead = read(m_pipe[READ], buf, bufSize-1);
#else
bytesRead = read(m_pipe[READ], buf, bufSize-1);
#endif
if (bytesRead > 0)
{
buf[bytesRead] = 0;
m_captured += buf;
}
else if (bytesRead < 0)
{
fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
while(fd_blocked || bytesRead == (bufSize-1));
secure_close(m_oldStdOut);
secure_close(m_oldStdErr);
secure_close(m_pipe[READ]);
#ifdef _MSC_VER
secure_close(m_pipe[WRITE]);
#endif
m_capturing = false;
return true;
}
std::string StdCapture::GetCapture()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_captured;
}
int StdCapture::secure_dup(int src)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup(src);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
return ret;
}
void StdCapture::secure_pipe(int * pipes)
{
int ret = -1;
bool fd_blocked = false;
do
{
#ifdef _MSC_VER
ret = pipe(pipes, 65536, O_BINARY);
#else
ret = pipe(pipes) == -1;
#endif
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
}
void StdCapture::secure_dup2(int src, int dest)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup2(src,dest);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
}
void StdCapture::secure_close(int & fd)
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = close(fd);
fd_blocked = (errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
while (ret < 0);
fd = -1;
}

Related

macOS iostream on a pseudo tty does not work

GCC has a stdio_filebuf extension that lets you wrap an iostream around a file descriptor while clang's basic_filebuf has an __open() method that lets open a file descriptor. I've tested both on regular files on both linux and macos. Works as expected.
However, for a pseudo tty file descriptor, it works for linux but not macos. I've confirmed that the file descriptor on macos does work by using read(2) on it. Here's my code.
#include <fmt/format.h>
#include <getopt.h>
#include <vector>
#include <functional>
#include <future>
#include <fcntl.h>
#include <cstdlib>
#if defined(__linux__)
#include <ext/stdio_filebuf.h>
#elif defined(__APPLE__)
#include <fstream>
#else
#error "unsupported os"
#endif
#include <iostream>
#include <unistd.h>
int get_ptym(int oflags)
{
int fd;
if ((fd = posix_openpt(oflags)) == -1
or grantpt(fd) == -1
or unlockpt(fd) == -1)
return -1;
return fd;
}
ssize_t getdelim(int fd, char *buf, size_t bufsz,
char delim='\n', bool store_delim=false)
{
size_t j = 0;
while (j < bufsz-2) {
char c;
ssize_t rc;
if ((rc = ::read(fd, &c, 1)) == -1) return rc;
if (rc == 0) break;
if (c != delim) { buf[j++] = c; continue; }
// Break if we get the delimiter.
if (store_delim) buf[j++] = c;
break;
}
buf[j] = '\0';
return j+1;
}
std::string m0(int fd)
{
#if defined(__linux__)
__gnu_cxx::stdio_filebuf<char> fb(fd, std::ios::in|std::ios::out);
std::iostream ioob(&fb);
#endif
#if defined(__APPLE__)
std::basic_filebuf<char> fb;
std::iostream ioob(fb.__open(fd, std::ios::in|std::ios::out));
#endif
std::string s;
if (!std::getline(ioob, s, '\n').good())
throw std::runtime_error("m0: getline failed");
return s;
}
std::string m1(int fd)
{
char buf[4096];
ssize_t nrd;
if ((nrd = getdelim(fd, buf, sizeof(buf))) == -1)
throw std::runtime_error("getlim error");
return std::string(buf);
}
void testing(const std::string &foonm, std::function<std::string(int)> foo)
{
using namespace std::chrono_literals;
int mfd, sfd;
// Open pty master and slave.
if ((mfd = get_ptym(O_RDWR)) == -1) return;
if ((sfd = open(ptsname(mfd), O_RDWR)) == -1) return;
#if defined(__linux__)
__gnu_cxx::stdio_filebuf<char> fb(sfd, std::ios::in|std::ios::out);
std::iostream ioob(&fb);
#endif
#if defined(__APPLE__)
std::basic_filebuf<char> fb;
std::iostream ioob(fb.__open(sfd, std::ios::in|std::ios::out));
#endif
fmt::print("testing {}\n", foonm);
std::string s;
if (!std::getline(std::cin, s, '\n').good()) {
fmt::print("{}: getline failed\n", __func__);
return;
}
auto f = std::async(std::launch::async, foo, mfd);
ioob << s << '\n';
ioob.flush();
try {
std::string fget;
std::future_status fstatus;
fstatus = f.wait_for(8s);
switch (fstatus) {
case std::future_status::timeout:
fmt::print("{}: f.wait_for(8s) timed out\n", __func__);
break;
case std::future_status::ready:
fget = f.get();
fmt::print("{}: fstatus ready, f.get={}\n", __func__, fget);
break;
default:
fmt::print("{}: fstatus default??\n", __func__);
}
}
catch (std::exception &e) {
fmt::print("{}: exception \"{}\"\n", __func__, e.what());
}
}
int main(int argc, char *argv[])
{
testing("m1", m1);
testing("m0", m0);
return 0;
}
Any ideas?

config.h: No such file or directory in ssh_client from libssh's example

When I compile ssh_client code from example folder of libssh source directory( I have wrote about building process of this library in this link : libssh's functions couldn't be found on qt):
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "examples_common.h"
#define MAXCMD 10
static char *host = NULL;
static char *user = NULL;
static char *cmds[MAXCMD];
static char *config_file = NULL;
static struct termios terminal;
static char *pcap_file = NULL;
static char *proxycommand;
static int auth_callback(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata)
{
(void) verify;
(void) userdata;
return ssh_getpass(prompt, buf, len, echo, verify);
}
struct ssh_callbacks_struct cb = {
.auth_function = auth_callback,
.userdata = NULL,
};
static void add_cmd(char *cmd)
{
int n;
for (n = 0; (n < MAXCMD) && cmds[n] != NULL; n++);
if (n == MAXCMD) {
return;
}
cmds[n] = strdup(cmd);
}
static void usage(void)
{
fprintf(stderr,
"Usage : ssh [options] [login#]hostname\n"
"sample client - libssh-%s\n"
"Options :\n"
" -l user : log in as user\n"
" -p port : connect to port\n"
" -d : use DSS to verify host public key\n"
" -r : use RSA to verify host public key\n"
" -F file : parse configuration file instead of default one\n"
#ifdef WITH_PCAP
" -P file : create a pcap debugging file\n"
#endif
#ifndef _WIN32
" -T proxycommand : command to execute as a socket proxy\n"
#endif
"\n",
ssh_version(0));
exit(0);
}
static int opts(int argc, char **argv)
{
int i;
while((i = getopt(argc,argv,"T:P:F:")) != -1) {
switch(i){
case 'P':
pcap_file = optarg;
break;
case 'F':
config_file = optarg;
break;
#ifndef _WIN32
case 'T':
proxycommand = optarg;
break;
#endif
default:
fprintf(stderr, "Unknown option %c\n", optopt);
usage();
}
}
if (optind < argc) {
host = argv[optind++];
}
while(optind < argc) {
add_cmd(argv[optind++]);
}
if (host == NULL) {
usage();
}
return 0;
}
#ifndef HAVE_CFMAKERAW
static void cfmakeraw(struct termios *termios_p)
{
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
}
#endif
static void do_cleanup(int i)
{
/* unused variable */
(void) i;
tcsetattr(0, TCSANOW, &terminal);
}
static void do_exit(int i)
{
/* unused variable */
(void) i;
do_cleanup(0);
exit(0);
}
static ssh_channel chan;
static int signal_delayed = 0;
static void sigwindowchanged(int i)
{
(void) i;
signal_delayed = 1;
}
static void setsignal(void)
{
signal(SIGWINCH, sigwindowchanged);
signal_delayed = 0;
}
static void sizechanged(void)
{
struct winsize win = {
.ws_row = 0,
};
ioctl(1, TIOCGWINSZ, &win);
ssh_channel_change_pty_size(chan,win.ws_col, win.ws_row);
setsignal();
}
static void select_loop(ssh_session session,ssh_channel channel)
{
ssh_connector connector_in, connector_out, connector_err;
int rc;
ssh_event event = ssh_event_new();
/* stdin */
connector_in = ssh_connector_new(session);
ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT);
ssh_connector_set_in_fd(connector_in, 0);
ssh_event_add_connector(event, connector_in);
/* stdout */
connector_out = ssh_connector_new(session);
ssh_connector_set_out_fd(connector_out, 1);
ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT);
ssh_event_add_connector(event, connector_out);
/* stderr */
connector_err = ssh_connector_new(session);
ssh_connector_set_out_fd(connector_err, 2);
ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR);
ssh_event_add_connector(event, connector_err);
while (ssh_channel_is_open(channel)) {
if (signal_delayed) {
sizechanged();
}
rc = ssh_event_dopoll(event, 60000);
if (rc == SSH_ERROR) {
fprintf(stderr, "Error in ssh_event_dopoll()\n");
break;
}
}
ssh_event_remove_connector(event, connector_in);
ssh_event_remove_connector(event, connector_out);
ssh_event_remove_connector(event, connector_err);
ssh_connector_free(connector_in);
ssh_connector_free(connector_out);
ssh_connector_free(connector_err);
ssh_event_free(event);
}
static void shell(ssh_session session)
{
ssh_channel channel;
struct termios terminal_local;
int interactive=isatty(0);
channel = ssh_channel_new(session);
if (channel == NULL) {
return;
}
if (interactive) {
tcgetattr(0, &terminal_local);
memcpy(&terminal, &terminal_local, sizeof(struct termios));
}
if (ssh_channel_open_session(channel)) {
printf("Error opening channel : %s\n", ssh_get_error(session));
ssh_channel_free(channel);
return;
}
chan = channel;
if (interactive) {
ssh_channel_request_pty(channel);
sizechanged();
}
if (ssh_channel_request_shell(channel)) {
printf("Requesting shell : %s\n", ssh_get_error(session));
ssh_channel_free(channel);
return;
}
if (interactive) {
cfmakeraw(&terminal_local);
tcsetattr(0, TCSANOW, &terminal_local);
setsignal();
}
signal(SIGTERM, do_cleanup);
select_loop(session, channel);
if (interactive) {
do_cleanup(0);
}
ssh_channel_free(channel);
}
static void batch_shell(ssh_session session)
{
ssh_channel channel;
char buffer[1024];
size_t i;
int s = 0;
for (i = 0; i < MAXCMD && cmds[i]; ++i) {
s += snprintf(buffer + s, sizeof(buffer) - s, "%s ", cmds[i]);
free(cmds[i]);
cmds[i] = NULL;
}
channel = ssh_channel_new(session);
if (channel == NULL) {
return;
}
ssh_channel_open_session(channel);
if (ssh_channel_request_exec(channel, buffer)) {
printf("Error executing '%s' : %s\n", buffer, ssh_get_error(session));
ssh_channel_free(channel);
return;
}
select_loop(session, channel);
ssh_channel_free(channel);
}
static int client(ssh_session session)
{
int auth = 0;
char *banner;
int state;
if (user) {
if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
return -1;
}
}
if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
return -1;
}
if (proxycommand != NULL) {
if (ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, proxycommand)) {
return -1;
}
}
/* Parse configuration file if specified: The command-line options will
* overwrite items loaded from configuration file */
if (config_file != NULL) {
ssh_options_parse_config(session, config_file);
} else {
ssh_options_parse_config(session, NULL);
}
if (ssh_connect(session)) {
fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
return -1;
}
state = verify_knownhost(session);
if (state != 0) {
return -1;
}
ssh_userauth_none(session, NULL);
banner = ssh_get_issue_banner(session);
if (banner) {
printf("%s\n", banner);
free(banner);
}
auth = authenticate_console(session);
if (auth != SSH_AUTH_SUCCESS) {
return -1;
}
if (cmds[0] == NULL) {
shell(session);
} else {
batch_shell(session);
}
return 0;
}
static ssh_pcap_file pcap;
static void set_pcap(ssh_session session)
{
if (pcap_file == NULL) {
return;
}
pcap = ssh_pcap_file_new();
if (pcap == NULL) {
return;
}
if (ssh_pcap_file_open(pcap, pcap_file) == SSH_ERROR) {
printf("Error opening pcap file\n");
ssh_pcap_file_free(pcap);
pcap = NULL;
return;
}
ssh_set_pcap_file(session, pcap);
}
static void cleanup_pcap(void)
{
if (pcap != NULL) {
ssh_pcap_file_free(pcap);
}
pcap = NULL;
}
int main(int argc, char **argv)
{
ssh_session session;
session = ssh_new();
ssh_callbacks_init(&cb);
ssh_set_callbacks(session,&cb);
if (ssh_options_getopt(session, &argc, argv)) {
fprintf(stderr,
"Error parsing command line: %s\n",
ssh_get_error(session));
usage();
}
opts(argc, argv);
signal(SIGTERM, do_exit);
set_pcap(session);
client(session);
ssh_disconnect(session);
ssh_free(session);
cleanup_pcap();
ssh_finalize();
return 0;
}
I got the following error:
/home/heydari.f/projects/ssh_client/ssh_client/main.c:100: error: config.h: No such file or directory
#include "config.h"
^~~~~~~~~~
Is there anything to do with configure or what?
I use Qt and my os is ubuntu 18.04.
Config headers as config.h normally aren't needed to compile a program against a library. Those are generated to compile the library, not the programs that link against them. If not, there will be lot of trouble as lots of software use them and there would be lots of collisions between them.
Being an example, it may be that it uses the config.h, but in that case I'm pretty sure you should compile with the system libssh uses to compile. (You may need to specify an option to compile examples when calling configure or specify something in DefineOptions.cmake or something in the same line.)
If you copied the sources (as it seems as the error states projects/ssh_client/) to build with Qt, you probably can remove that include unless it is a config from Qt itself.
Also, if you are compiling with Qt you surely need to install the lib and follow #Dmitry advice about -I, -L and -l flags to compiler.

Interactive Brokers C++ Error: error: 'min' was not declared in this scope

I am trying to setup the Interactive Brokers API on Ubuntu (18.04). I have installed both the IB Gateway, which is used for communicating with exchanges, as well as other API software for developing trading algorithms in Java, C++, C# and Python. (Which you can find here). The API is written in both Java and C++, and as stated prior it offers support for both. However when attempting to compile an example from their source code there is an error in the EReader.cpp file. I have dealt with several other C++ errors in their code however this one I cannot figure out. Here is the code:
#include "StdAfx.h"
#include "shared_ptr.h"
#include "Contract.h"
#include "EDecoder.h"
#include "EMutex.h"
#include "EReader.h"
#include "EClientSocket.h"
#include "EPosixClientSocketPlatform.h"
#include "EReaderSignal.h"
#include "EMessage.h"
#include "DefaultEWrapper.h"
#define IN_BUF_SIZE_DEFAULT 8192
static DefaultEWrapper defaultWrapper;
EReader::EReader(EClientSocket *clientSocket, EReaderSignal *signal)
: processMsgsDecoder_(clientSocket->EClient::serverVersion(), clientSocket->getWrapper(), clientSocket) {
m_isAlive = true;
m_pClientSocket = clientSocket;
m_pEReaderSignal = signal;
m_needsWriteSelect = false;
m_nMaxBufSize = IN_BUF_SIZE_DEFAULT;
m_buf.reserve(IN_BUF_SIZE_DEFAULT);
}
EReader::~EReader(void) {
m_isAlive = false;
#if defined(IB_WIN32)
WaitForSingleObject(m_hReadThread, INFINITE);
#endif
}
void EReader::checkClient() {
m_needsWriteSelect = !m_pClientSocket->getTransport()-
isOutBufferEmpty();
}
void EReader::start() {
#if defined(IB_POSIX)
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create( &thread, &attr, readToQueueThread, this );
pthread_attr_destroy(&attr);
#elif defined(IB_WIN32)
m_hReadThread = CreateThread(0, 0, readToQueueThread, this, 0, 0);
#else
# error "Not implemented on this platform"
#endif
}
#if defined(IB_POSIX)
void * EReader::readToQueueThread(void * lpParam)
#elif defined(IB_WIN32)
DWORD WINAPI EReader::readToQueueThread(LPVOID lpParam)
#else
# error "Not implemented on this platform"
#endif
{
EReader *pThis = reinterpret_cast<EReader *>(lpParam);
pThis->readToQueue();
return 0;
}
void EReader::readToQueue() {
EMessage *msg = 0;
while (m_isAlive) {
if (m_buf.size() == 0 && !processNonBlockingSelect() && m_pClientSocket->isSocketOK())
continue;
if (!putMessageToQueue())
break;
}
m_pClientSocket->handleSocketError();
m_pEReaderSignal->issueSignal(); //letting client know that socket was closed
}
bool EReader::putMessageToQueue() {
EMessage *msg = 0;
if (m_pClientSocket->isSocketOK())
msg = readSingleMsg();
if (msg == 0)
return false;
m_csMsgQueue.Enter();
m_msgQueue.push_back(ibapi::shared_ptr<EMessage>(msg));
m_csMsgQueue.Leave();
m_pEReaderSignal->issueSignal();
return true;
}
bool EReader::processNonBlockingSelect() {
fd_set readSet, writeSet, errorSet;
struct timeval tval;
tval.tv_usec = 100 * 1000; //100 ms
tval.tv_sec = 0;
if( m_pClientSocket->fd() >= 0 ) {
FD_ZERO( &readSet);
errorSet = writeSet = readSet;
FD_SET( m_pClientSocket->fd(), &readSet);
if (m_needsWriteSelect)
FD_SET( m_pClientSocket->fd(), &writeSet);
FD_SET( m_pClientSocket->fd(), &errorSet);
int ret = select( m_pClientSocket->fd() + 1, &readSet, &writeSet, &errorSet, &tval);
if( ret == 0) { // timeout
return false;
}
if( ret < 0) { // error
m_pClientSocket->eDisconnect();
return false;
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &errorSet)) {
// error on socket
m_pClientSocket->onError();
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &writeSet)) {
// socket is ready for writing
onSend();
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &readSet)) {
// socket is ready for reading
onReceive();
}
return true;
}
return false;
}
void EReader::onSend() {
m_pEReaderSignal->issueSignal();
}
void EReader::onReceive() {
int nOffset = m_buf.size();
m_buf.resize(m_nMaxBufSize);
int nRes = m_pClientSocket->receive(m_buf.data() + nOffset, m_buf.size() - nOffset);
if (nRes <= 0)
return;
m_buf.resize(nRes + nOffset);
}
bool EReader::bufferedRead(char *buf, int size) {
while (size > 0) {
while (m_buf.size() < size && m_buf.size() < m_nMaxBufSize)
if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
return false;
int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);
std::copy(m_buf.begin(), m_buf.begin() + nBytes, buf);
std::copy(m_buf.begin() + nBytes, m_buf.end(), m_buf.begin());
m_buf.resize(m_buf.size() - nBytes);
size -= nBytes;
buf += nBytes;
}
return true;
}
EMessage * EReader::readSingleMsg() {
if (m_pClientSocket->usingV100Plus()) {
int msgSize;
if (!bufferedRead((char *)&msgSize, sizeof(msgSize)))
return 0;
msgSize = htonl(msgSize);
if (msgSize <= 0 || msgSize > MAX_MSG_LEN)
return 0;
std::vector<char> buf = std::vector<char>(msgSize);
if (!bufferedRead(buf.data(), buf.size()))
return 0;
return new EMessage(buf);
}
else {
const char *pBegin = 0;
const char *pEnd = 0;
int msgSize = 0;
while (msgSize == 0)
{
if (m_buf.size() >= m_nMaxBufSize * 3/4)
m_nMaxBufSize *= 2;
if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
return 0;
pBegin = m_buf.data();
pEnd = pBegin + m_buf.size();
msgSize = EDecoder(m_pClientSocket->EClient::serverVersion(), &defaultWrapper).parseAndProcessMsg(pBegin, pEnd);
}
std::vector<char> msgData(msgSize);
if (!bufferedRead(msgData.data(), msgSize))
return 0;
if (m_buf.size() < IN_BUF_SIZE_DEFAULT && m_buf.capacity() > IN_BUF_SIZE_DEFAULT)
{
m_buf.resize(m_nMaxBufSize = IN_BUF_SIZE_DEFAULT);
m_buf.shrink_to_fit();
}
EMessage * msg = new EMessage(msgData);
return msg;
}
}
ibapi::shared_ptr<EMessage> EReader::getMsg(void) {
m_csMsgQueue.Enter();
if (m_msgQueue.size() == 0) {
m_csMsgQueue.Leave();
return ibapi::shared_ptr<EMessage>();
}
ibapi::shared_ptr<EMessage> msg = m_msgQueue.front();
m_msgQueue.pop_front();
m_csMsgQueue.Leave();
return msg;
}
void EReader::processMsgs(void) {
m_pClientSocket->onSend();
checkClient();
ibapi::shared_ptr<EMessage> msg = getMsg();
if (!msg.get())
return;
const char *pBegin = msg->begin();
while (processMsgsDecoder_.parseAndProcessMsg(pBegin, msg->end()) > 0)
{
msg = getMsg();
if (!msg.get())
break;
pBegin = msg->begin();
}
}
The error I get it is the following:
error: 'min' was not declared in this scope int nBytes =
min(m_nMaxBuffSize, size);
I have had to do other things such as editing other source code and makefiles, I am stuck here. Any insight would be appreciated.
In my version 973 source at that line I have
int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);
Make sure you are using the latest version. The problem may be an example of what happens here Why is "using namespace std" considered bad practice?

Why does ofstream not trigger IN_CREATE

I'm writing tests for an inotify library and I'm currently working on capturing the IN_CREATE event. I'm trying to use boost::filesystem::ofstream, which is essentially std::basic_ofstream, but I'm not getting the IN_CREATE event.
There are two threads in execution, the main thread, and one I create that processes events like so:
std::thread thread([&]()
{
boost::asio::io_service::work(g_io_service);
g_io_service.run();
}
Whenever an event is received, the library calls this function in the context of the above thread:
[&](event_type event, std::string const & path)
{
std::lock_guard<std::mutex> lock(ready_mtx);
events.push_back(event);
condition.notify_all();
ready = true;
}
What the test looks like (main thread):
{
boost::filesystem::ofstream file(path);
file << "test";
}
{
std::lock_guard<std::mutex> lock(ready_mtx);
while (!ready)
condition.wait(lock);
REQUIRE(events[0] == event_type::FILE_CREATE);
}
I'm expecting an event for the creation and modification of the file, and the file is in fact being created when I look for it in the terminal, but I do not receive any event.
However, when I manually create the file with echo "test" > test.file I do receive the create and modify events.
Is there a reason for this behavior? Am I approaching this the wrong way?
I created the following without my library and it works just fine. I'm obviously doing something wrong in my own code. Thanks.
#include <atomic>
#include <condition_variable>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <thread>
#include <linux/limits.h>
#include <sys/inotify.h>
#include <unistd.h>
std::mutex cout_mtx;
std::string path("/tmp/test-inotify");
std::string filename("test.file");
void print(std::string const & msg)
{
std::lock_guard<std::mutex> lock(cout_mtx);
std::cout << msg << std::endl;
}
void thread_fn(int fd, std::function<void(int, std::string const & name)> callback)
{
std::string buffer;
buffer.resize(64 * (sizeof(inotify_event) + NAME_MAX + 1));
while (true)
{
int bytes_read = ::read(fd, &buffer[0], buffer.size());
if (bytes_read == -1)
{
if (errno != EAGAIN)
{
print("Fatal error to call read");
break;
}
}
int offset = 0;
inotify_event const * event = nullptr;
for (; offset < bytes_read; offset += sizeof(inotify_event) + event->len)
{
event = reinterpret_cast<inotify_event const*>(&buffer[offset]);
if (event->mask & IN_IGNORED)
{
// rewatch
int wd = ::inotify_add_watch(fd, path.c_str(), IN_CREATE);
if (wd == -1)
{
print("Unable to rewatch directory");
break;
}
}
std::string name;
std::copy(&event->name[0], &event->name[event->len], std::back_inserter(name));
int event_value = event->mask & 0xffff;
callback(event_value, name);
}
}
}
int main()
{
int fd = ::inotify_init1(IN_NONBLOCK);
if (fd == -1)
{
print("inotifiy_init1 failed");
return errno;
}
int wd = ::inotify_add_watch(fd, path.c_str(), IN_CREATE);
if (wd == -1)
{
print("inotify_add_watch failed");
return errno;
}
std::atomic<bool> ready;
std::mutex ready_mtx;
std::condition_variable condition;
int first_event = -1;
std::thread thread([&]()
{
thread_fn(fd,
[&](int event, std::string const & name)
{
std::unique_lock<std::mutex> lock(ready_mtx);
print(std::to_string(event));
if (event == IN_CREATE)
{
first_event = event;
print(name + " was created");
}
condition.notify_all();
ready = true;
});
});
{
std::ofstream file(path + "/" + filename);
}
{
std::unique_lock<std::mutex> lock(ready_mtx);
while (!ready)
condition.wait(lock);
if (first_event == IN_CREATE)
print("success");
else
print("failure");
}
thread.join();
return 0;
}

Playing wav file with portaudio and sndfile

I have written a function to play a sound file using portaudio and sndfile. Unfortunately the sound quality is terrible. The sound is more like a hiss. The following is the source code of the function I am using.
#define _GLIBCXX_USE_C99_MATH 1
#include "PlaySound_config.h"
#include <boost/predef.h>
#if !defined(USE_PORTAUDIO) // {
# define USE_PORTAUDIO 0
# if (! BOOST_OS_CYGWIN && ! BOOST_OS_WINDOWS) // {
# undef USE_PORTAUDIO
# define USE_PORTAUDIO 1
# endif // }
#endif // }
#if (PLAY_SOUND_HAVE_PORTAUDIO_H && PLAY_SOUND_HAVE_SNDFILE_H && PLAY_SOUND_HAVE_SNDFILE_HH && USE_PORTAUDIO) // {
#if (PLAY_SOUND_HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#include <sndfile.hh>
#include <cmath>
#include <fstream>
#include <iostream>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include "PlaySound.h"
#include "PlaySoundStrings.h"
void SoundWarning(const std::string& message)
{
std::cerr << message << std::endl;
}
bool PlaySoundFile(const std::string& soundFile, unsigned long /* volume */)
{
const int MAX_CHANNELS = 1;
const double SAMPLE_RATE = 11025.0;
const unsigned long FRAMES_PER_BUFFER = 1024;
const size_t BUFFER_LEN = 1024;
using boost::format;
using boost::io::group;
std::string message;
if (soundFile.empty())
{
errno = EINVAL;
message = playSoundStrings[error_invalid_argument];
SoundWarning(message);
return false;
}
boost::filesystem::path soundFilePath(soundFile);
if (! boost::filesystem::exists(soundFilePath))
{
errno = EINVAL;
message = str(format(playSoundStrings[error_file_does_not_exist]) % soundFile.c_str());
SoundWarning(message);
return false;
}
PaError paError = Pa_Initialize();
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_initialize_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
return false;
}
SNDFILE* sndFile;
SF_INFO sfInfo;
sndFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo);
if (! sndFile)
{
message = str(format(playSoundStrings[error_sf_open_failed]) % soundFile.c_str() % sf_strerror(nullptr));
SoundWarning(message);
Pa_Terminate();
return false;
}
if (sfInfo.channels > MAX_CHANNELS)
{
message = str(format(playSoundStrings[error_too_many_channels]) % sfInfo.channels % MAX_CHANNELS);
SoundWarning(message);
Pa_Terminate();
return false;
}
PaStream* stream = nullptr;
PaStreamParameters paStreamParameters;
paStreamParameters.device = Pa_GetDefaultOutputDevice();
paStreamParameters.channelCount = sfInfo.channels;
paStreamParameters.sampleFormat = paInt16;
paStreamParameters.suggestedLatency = Pa_GetDeviceInfo(paStreamParameters.device)->defaultLowOutputLatency;
paStreamParameters.hostApiSpecificStreamInfo = nullptr;
paError = Pa_OpenStream(
&stream, nullptr, &paStreamParameters,
SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff,
nullptr, nullptr);
if (paError != paNoError || ! stream)
{
message = str(format(playSoundStrings[error_pa_open_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
paError = Pa_StartStream(stream);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_start_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
sf_count_t readCount = 0;
double data[BUFFER_LEN];
while ((readCount = sf_read_double(sndFile, data, BUFFER_LEN)))
{
paError = Pa_WriteStream(stream, data, BUFFER_LEN);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_write_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
break;
}
}
paError = Pa_CloseStream(stream);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_close_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
Pa_Terminate();
return true;
}
I saw some sample code in the article What is a lightweight cross platform WAV playing library? but the sample is incomplete. It appears that it will only play the first five seconds of the file. I want to play the entire file.
Any idea what I am doing wrong?
This code is part of my PlaySound project.
I made several mistakes in the original version of my code. The first was in the line in which I initialized the sampleFormat member of the PaStreamParameters structure.
In my original code I initialized this member as follows.
paStreamParameters.sampleFormat = paInt16;
I should have initialized it as follows.
paStreamParameters.sampleFormat = paInt32;
My next mistake was in the call to the Pa_OpenStream function. I set the sampleRate parameter to a hard coded constant, in this case 11025.0. I should have set it to the value of the samplerate member of the SF_INFO structure.
My third mistake was to use the sf_read_double function to read from the sound file. In several working samples I eventually found, including the sndfile-play application, the sf_read_float function is used instead.
My forth mistake is that I did not scale the data read from the sound file before passing it to the Pa_WriteStream function. I found the code to scale the data in the source code of the sndfile-play application.
For anyone who is interested, the final version of my source code is as follows.
#define _GLIBCXX_USE_C99_MATH 1
#include "PlaySound_config.h"
#include <boost/predef.h>
#if !defined(USE_PORTAUDIO) // {
# define USE_PORTAUDIO 0
# if (! BOOST_OS_CYGWIN && ! BOOST_OS_WINDOWS) // {
# undef USE_PORTAUDIO
# define USE_PORTAUDIO 1
# endif // }
#endif // }
#if (PLAY_SOUND_HAVE_PORTAUDIO_H && PLAY_SOUND_HAVE_SNDFILE_H && PLAY_SOUND_HAVE_SNDFILE_HH && USE_PORTAUDIO) // {
#if (PLAY_SOUND_HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <portaudio.h>
#include <sndfile.hh>
#include <cmath>
#include <fstream>
#include <iostream>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include "PlaySound.h"
#include "PlaySoundStrings.h"
void SoundWarning(const std::string& message)
{
std::cerr << message << std::endl;
}
bool PlaySoundFile(const std::string& soundFile, unsigned long /* volume */)
{
const int MAX_CHANNELS = 1;
const size_t BUFFER_LEN = 1024;
using boost::format;
using boost::io::group;
std::string message;
if (soundFile.empty())
{
errno = EINVAL;
message = playSoundStrings[error_invalid_argument];
SoundWarning(message);
return false;
}
boost::filesystem::path soundFilePath(soundFile);
if (! boost::filesystem::exists(soundFilePath))
{
errno = EINVAL;
message = str(format(playSoundStrings[error_file_does_not_exist]) % soundFile.c_str());
SoundWarning(message);
return false;
}
PaError paError = Pa_Initialize();
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_initialize_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
return false;
}
SNDFILE* sndFile = nullptr;
SF_INFO sfInfo;
::memset(&sfInfo, 0, sizeof(sfInfo));
sndFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo);
if (! sndFile)
{
message = str(format(playSoundStrings[error_sf_open_failed]) % soundFile.c_str() % sf_strerror(nullptr));
SoundWarning(message);
Pa_Terminate();
return false;
}
if (sfInfo.channels > MAX_CHANNELS)
{
message = str(format(playSoundStrings[error_too_many_channels]) % sfInfo.channels % MAX_CHANNELS);
SoundWarning(message);
Pa_Terminate();
return false;
}
PaStream* stream = nullptr;
PaStreamParameters paStreamParameters;
paStreamParameters.device = Pa_GetDefaultOutputDevice();
paStreamParameters.channelCount = sfInfo.channels;
paStreamParameters.sampleFormat = paInt32;
paStreamParameters.suggestedLatency = Pa_GetDeviceInfo(paStreamParameters.device)->defaultLowOutputLatency;
paStreamParameters.hostApiSpecificStreamInfo = nullptr;
paError = Pa_OpenStream(
&stream, nullptr, &paStreamParameters,
sfInfo.samplerate, paFramesPerBufferUnspecified, paClipOff,
nullptr, nullptr);
if (paError != paNoError || ! stream)
{
message = str(format(playSoundStrings[error_pa_open_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
paError = Pa_StartStream(stream);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_start_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
int subFormat = sfInfo.format & SF_FORMAT_SUBMASK;
double scale = 1.0;
if (subFormat == SF_FORMAT_FLOAT || subFormat == SF_FORMAT_DOUBLE)
{
sf_command(sndFile, SFC_CALC_SIGNAL_MAX, &scale, sizeof(scale));
if (scale < 1e-10)
{
scale = 1.0;
}
else
{
scale = 32700.0 / scale;
}
}
sf_count_t readCount = 0;
float data[BUFFER_LEN];
::memset(data, 0, sizeof(data));
while ((readCount = sf_read_float(sndFile, data, BUFFER_LEN)))
{
if (subFormat == SF_FORMAT_FLOAT || subFormat == SF_FORMAT_DOUBLE)
{
int m = 0;
for (m = 0 ; m < readCount ; ++m)
{
data[m] *= scale;
}
}
paError = Pa_WriteStream(stream, data, BUFFER_LEN);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_write_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
break;
}
::memset(data, 0, sizeof(data));
}
paError = Pa_CloseStream(stream);
if (paError != paNoError)
{
message = str(format(playSoundStrings[error_pa_close_stream_failed]) % Pa_GetErrorText(paError));
SoundWarning(message);
Pa_Terminate();
return false;
}
Pa_Terminate();
return true;
}