write system call unix doesn't write all text from argv [duplicate] - c++

This question already has answers here:
Different answers from strlen and sizeof for Pointer & Array based init of String [duplicate]
(4 answers)
Closed 3 years ago.
I'm trying to write out data that the user entered into argv[2]. I have to use write() system call (unix)
for Example I enter "hi there" but "hi th" is written out into the file instead of the whole text.
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
using namespace std;
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
int fd, fileWrite;
char *file = argv[1]; //file to be create
char *text = argv[2]; //text stored here
fd = open(file, O_WRONLY | O_APPEND | O_CREAT);
//write message from command line
fileWrite = write(fd, text, sizeof(text));
fileWrite = write(fd, "\n", 1);
if(fileWrite == -1){
perror("fileWrite");
}
//close file
close(fd);
return 0;
}`

Use strlen(text) in<string.h> instead of sizeof(text) to determine the length of the string, sizeof(text) will return you the size of a pointer, which is always the same.

Related

C++ Read UTF-8 (Lithuanian letters) symbols from txt file and show them in console application [duplicate]

This question already has answers here:
Read Unicode UTF-8 file into wstring
(7 answers)
Closed 1 year ago.
I need you help.
I'm using Windows 10 and Visual Studio Community compiler.
I managed to get Lithuanian letter to show on C++ console application using wstring and wcout.
#include <iostream>
#include <io.h>
#include <fcntl.h>
using namespace std;
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
wstring a = L"ąėėąčėį";
wcout << a;
return 0;
}
Result is exactly what I wanted it to be
Now I want my program to read Lithuanian letters from Info.txt file.
This is how far I managed to get.
#include <iostream>
#include <fstream>
#include <io.h>
#include <fcntl.h>
#include <string>
using namespace std;
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
wstring text;
wifstream fin("Info.txt");
getline(fin, text);
wcout << text;
return 0;
}
Returned string in console application shows different simbols.
But the returned string in console application shows different simbols.
In my belief a possible solution
I need to add L before the text like in previous example with wcout.
wstring a = L"ąėėąčėį";
But I'm still just learning C++ and I don't know how to do so in example with Info.txt
I need your help!
UTF8 needs std::ifstream, not wifstream. The latter is used in Windows as UTF16 file storage (not recommended in any system)
You can use SetConsoleOutputCP(CP_UTF8) to enable UTF8 printing, but that can run in to problems, specially in C++ 20
Instead, call _setmode and convert UTF8 to UTF16.
Make sure notepad saves the file in UTF8 (encoding option is available in Save window)
#include <iostream>
#include <fstream>
#include <string>
#include <io.h>
#include <fcntl.h>
#include <Windows.h>
std::wstring u16(const std::string u8)
{
if (u8.empty()) return std::wstring();
int size = MultiByteToWideChar(CP_UTF8, 0, u8.c_str(), -1, 0, 0);
std::wstring u16(size, 0);
MultiByteToWideChar(CP_UTF8, 0, u8.c_str(), -1, u16.data(), size);
return u16;
}
int main()
{
(void)_setmode(_fileno(stdout), _O_U16TEXT);
std::string text;
std::ifstream fin("Info.txt");
if (fin)
while (getline(fin, text))
std::wcout << u16(text) << "\n";
return 0;
}

Non-blocking way to check if there is data on a iofstream

I need to have a way to check if there is data to read on a file (fifo) in a non-blocking way.
I have tried using peek; but it is blocking, I have tried to get and then unget a character in order to check the file without altering the contents; but once again get is blocking...
The only non-blocking solution I have found is to use std::getline(file, line_str) and check if the string is empty; however this does not suit my needs as it alters the data on the file. (The data is a serialized object I will read once I detect there is something to read).
Note: I need this to be non-blocking: I have multiple file streams and need to check all of them regularly to see if there is an object to read/deserialize.
Here is a simple example of what I am trying to achieve:
Sender.cpp:
#include <fstream>
#include <iostream>
#include <string>
extern "C"{
#include <sys/stat.h> // S_IRUSR, S_IWUSR, mkfifo
}
#include <cerrno> // errno
int main(int, char** argv) {
std::string pipe = "foobar";
if(mkfifo(pipe.c_str(), S_IRUSR | S_IWUSR) < 0){
if (errno != EEXIST){
std::cerr << errno;
}
}
std::ofstream file{pipe.c_str()};
file.write("boop", 4); // Simulated object serialization
}
Reader.cpp:
#include <fstream>
#include <iostream>
#include <string>
extern "C"{
#include <sys/stat.h> // S_IRUSR, S_IWUSR, mkfifo
}
#include <cerrno> // errno
int main(int, char** argv) {
std::string pipe = "foobar";
if(mkfifo(pipe.c_str(), S_IRUSR | S_IWUSR) < 0){
if (errno != EEXIST){
std::cerr << errno;
}
}
std::ifstream file{pipe.c_str()};
// ...
/* Do check for data and read/deserialize if any data */
// This is in some sort of loop that goes over the different
// filestreams and checks to see if they have data to treat
}
Any help is really appreciated...
EDIT:
Following Zoso's answer I tried using the file size to determine if the file had been changed; however attempeting to get the size of a fifo named pipe is not possible : filesystem error: cannot get file size: Operation not supported [myFilePath]
I'm not sure if this would work for your particular use case but you could use the filesystem APIs. A simple example is
#include <iostream>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
while (true) {
auto path = fs::current_path().append("test");
std::cout <<"Press enter to know file size of "<<path.c_str() <<'\n';
char c= getchar();
try {
std::cout<<"Size of "<<path.c_str()<<"is "<<fs::file_size(path)<<'\n';
} catch(fs::filesystem_error& e) {
std::cout << e.what() << '\n';
}
}
}
As and when the file gets data, that can be kept track of based on the increasing size and the data to be processed can be tracked as and when that data is consumed.

reading a UTF-16 file in Linux

I have the following program which reads a file into a string buffer.
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
constexpr int BUFSIZE = 1024;
int main(int argc, char *argv[])
{
std::ifstream ifs(argv[1], std::ifstream::binary);
if(!ifs)
return 1;
string buffer(BUFSIZE, L'\0');
ifs.read(&buffer[0], BUFSIZE);
cerr << ifs.gcount() << endl;
return 0;
}
It prints out the expected 1024.
The following program which is supposed to read into a wstring buffer doesn't work though.
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
constexpr int BUFSIZE = 1024;
int main(int argc, char *argv[])
{
std::wifstream ifs(argv[1], std::ifstream::binary);
if(!ifs)
return 1;
wstring buffer(BUFSIZE, L'\0');
ifs.read(&buffer[0], BUFSIZE);
cerr << ifs.gcount() << endl;
return 0;
}
Ir prints out 0 with the same file.
As you can see the only difference is changing the stream to a wstream and the buffer to a wstring.
I've tried both g++ 8.2.1 and clang++ 6.0.1 under OpenSUSE Tumbleweed.
Where is the problem/my error?
You should be using std::basic_ifstream<char16_t> and std::u16string for UTF-16. std::wifstream and std::wstring are not appropriate because the width of wchar_t is implementation defined. In Linux in particular, it is (usually?) 32 bits wide.
Same for character literals. You should use u'\0' etc. instead of L'\0'.

Segmentation Fault on string conversion?

I am working on an encryption project and I am making a simple test that takes a file name from terminal and runs my encryption. I have the following encryption code but I get the following segmentation fault:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid<br><br>
Program received signal SIGABRT, Aborted.
0x00007ffff74ab428 in __GI_raise (sig=sig#entry=6)
at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
After running a trace with gdb I have confirmed that this fault is triggered after the following loc:
string plain(reinterpret_cast(fileContents), fileLength);
My main function below calls this piece of code:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <fstream>
#include <limits>
#include <sstream>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rc4_enc.c"
#include "rc4_skey.c"
using namespace std;
void foo(int);
int main(int argc, char * argv[]){
if(argc!=2){
cout << "Please enter the filename that you want to encrypt or decrypt\n";
exit(2);
}
int fd;
fd = open(argv[1], O_RDONLY);
cout << "The file descriptor is " << fd << endl;
//close(fd);// Can I modify it if it's closed?
foo(fd);
return 0;
}
And the function is as follows:
void foo(int fd) {
int fileLength = lseek(fd, 0, SEEK_END);
unsigned char* fileContents;
fileContents = (unsigned char*) calloc(fileLength, sizeof(char));
pread(fd, fileContents, fileLength, 0);
string plain(reinterpret_cast<char*>(fileContents), fileLength); // Segfault happens here. But why?
RC4_KEY key;
int length = plain.size();
unsigned char *buf = (unsigned char*)malloc(length+1);
memset(buf, 0, length+1);
ifstream pass;
pass.open("pass.txt");
if(!pass.good()){
return;
}
else{
string password;
getline(pass,password);
RC4_set_key(&key, password.length(), (const unsigned char*)password.c_str());
}
RC4(&key, length, (const unsigned char*)plain.c_str(), buf);
string result((char*)buf, length);
free(buf);
const char *outputBuf = result.c_str();
pwrite(fd, outputBuf, result.length(), 0);
ftruncate(fd, result.length());
}
I would leave this as a comment, but do not have enough reputation.
How large is the file? Could calloc be failing and returning NULL?
Even if it's not that, might be wise to check calloc's return value. Or use the new operator, with try/catch.

C++ piping issue

I am trying to to fork my c++ program and direct the parents output into the childs input, I am using pipe() and fork(). In the directory of the program there is a file called input.txt. Unfortunately the only output I get is "wc: stdin: read: Bad file descriptor". Does anyone know why this is? If so what am I doing wrong? Thanks
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main(int argc, char *argv[]){
int pipes[2],pid,stdIn,stdOut;
stdIn = dup(0);
stdOut = dup(1);
pipe(pipes);
pid = fork();
if(pid == 0){
dup2(pipes[1],0);
close(pipes[1]);
execlp("wc","wc",NULL);
}
else{
dup2(pipes[0],1);
close(pipes[0]);
std::cout<<"input.txt"<<std::endl;
dup2(stdOut,0);
std::cout<<"parent done\n";
wait(NULL);
}
std::cout<<"after"<<std::endl;
return 0;
}
There are several things that should be fixed in your program:
Use STDIN_FILENO and STDOUT_FILENO instead of 0 and 1. This values may change on different platforms and you have also made a mistake which could probably be avoided if you've used names instead of value, e.g. dup2(stdOut,0); duplicated stdin and you need to duplicate stdout here.
You should close write end of the pipe in both child and parent.
By making wc read from stdin, you are then passing "input.txt" string to it - it will return stats for that string, not for the file. You could either fix it be opening a file descriptor for that file or using exec* with cat.
None of your calls the functions like pipe() or execlp() checks for failure. You should do it like that:
if (pipe(pipes) == -1) {
perror("pipe");
exit(1);
}
You don't need stdIn variable.
You will find fixed code below (it does not implement what I've described in the (5) though):
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int pipes[2], pid, stdOut;
stdOut = dup(STDOUT_FILENO);
pipe(pipes);
pid = fork();
if (pid == 0) {
dup2(pipes[0], STDIN_FILENO);
/* You need to close write end of the pipe here */
close(pipes[1]);
execlp("wc", "wc", NULL);
} else {
std::cout << "Parent setup" << std::endl;
dup2(pipes[1], STDOUT_FILENO);
/* You need to close write end of the pipe here as well */
close(pipes[1]);
/* This will only send the string "input.txt" through the pipe, to the
* wc command */
std::cout << "input.txt" << std::endl;
dup2(stdOut, STDOUT_FILENO);
std::cout << "Parent done" << std::endl;
wait(NULL);
}
std::cout << "Program finished" << std::endl;
return 0;
}
EDIT: As suggested in the comment to the other answer, you could simple use xargs wc to read stdint as file argument:
execlp("xargs", "xargs","wc",NULL);
You have the pipe backwards, you have connected the write end of the pipe to the standard input of wc. You will need to close the write end of the pipe in both processes before wc will detect an end of file condition and terminate normally.
You also incorrectly restore the original standard output to the standard input of the parent.
Furthermore wc will by default not interpret standard input as a list filenames and will therefore not read input.txt.