Whats an easy way I can store the password the user inputs while still keeping the password hidden?
char password[9];
int i;
printf("Enter your password: ");
for (i=0;i<9;i++)
{
password[i] = getch();
printf("*");
}
for (i=0;i<9;i++)
printf("%c",password[i]);
getch();
}
I wanna store the password so I can do a simple if (password[i] == root_password) so the proper password continues on.
Your problem appears to be that you're not checking for newline '\n' nor end-of-file.
printf("Enter your password: ");
char password[9];
int i;
for (i = 0; i < sizeof password - 1; i++)
{
int c = getch();
if (c == '\n' || c == EOF)
break;
}
password[i] = c;
printf("*");
}
password[i] = '\0';
This way, password will end up being an ASCIIZ string, suitable for printing with puts, printf("%s", password), or - crucially...
if (strcmp(password, root_password)) == 0)
your_wish_is_my_command();
Note that we read at most 8 characters into the password as we need one extra character for the NUL terminator. You could increase that if you wanted.
You need to use the console API in Windows. Below is a snippet that disables echoing in the console window. The function SetConsoleMode() is used to control the echoing (among other things). I save the old mode, so that I can restore the console once the password has been retrieved.
Also, the *ConsoleMode() functions need a handle to the console input buffer. How you can get a handle to these buffers is described in the MSDN documentation of CreateFile().
int main(int argc, char* argv[])
{
char password[100] = { 0 };
printf("Enter your password: ");
HANDLE hConsole = ::CreateFile("CONIN$", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
DWORD dwOldMode;
::GetConsoleMode(hConsole, &dwOldMode);
::SetConsoleMode(hConsole, dwOldMode & ~ENABLE_ECHO_INPUT);
bool bFinished = false;
while(!bFinished) {
if(!fgets(password, sizeof(password) / sizeof(password[0]) - 1, stdin)) {
printf("\nEOF - exiting\n");
} else
bFinished = true;
}
::SetConsoleMode(hConsole, dwOldMode | ENABLE_ECHO_INPUT);
printf("\nPassword is: %s\n", password);
return 0;
}
Since we are in C++ and Windows do this:
#include <iostream>
#include <string>
#include <conio.h> //_getch
#include <Windows.h> //VK_RETURN = 0x0D
using namespace std;
string read_password()
{
string pass;
cout << "Enter your password: ";
int character = 0;
while(VK_RETURN != (character = _getch()) )
{
cout << '*';
pass += static_cast<char>(character);
}
cout << std::endl;
return pass;
}
int main ()
{
string root_password = "anypass123";
string pass = read_password();
if (pass == root_password)
{
cout << "password accepted" << endl;
}
return 0;
}
compiled & tested
Related
I've been stuck on an issue with my program and just hoping for any help at this point :(
or guidance towards the right direction. In my code, I'm implenting a mini shell in c++ where the user can pipe 2 or more processes together, yet an issue keeps coming up whenever I execute it. Only the first and last commands actually execute so say I run:
cat b.txt | sort | tail -2
only cat b.txt and tail -2 would execute.
Here is my attempt at the whole program, also referenced to this which helped me tremendously with the setup.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <sys/utsname.h>
#include <unistd.h>
using namespace std;
//this variable will take in the line of input submitted by the user
char buf[1024];
//PIDs for the two child processes
pid_t pid[300];
//these will be use to check the status of each child in the parent process
int status;
int status2;
int pid_num = 1;
//initializes the pipe
int pipeA[2] = {-1,-1};
int g = 0;
void first_command(int pipeA[], char * command[], bool pipeExists){
if(pipeExists){
dup2(pipeA[1], 1);
close(pipeA[0]);
}
// this will run command[0] as the file to execute, and command as the arg
execvp(command[0], command);
printf("Can not execute FIRST command, please enter a valid command \n");
exit(127);
}
void other_command(int pipeA[], char * command0[], int index){
dup2(pipeA[0], 0);
close(pipeA[1]);
execvp(command0[0], command0);
printf("Can not execute SECOND command, please enter a valid command\n");
exit(127);
}
void main_func() {
//stay inside the loop and keep asking the user for input until the user quits the program
while (fgets(buf,1024,stdin) != NULL){
//initialize a boolean to check if user wants to pipe something, set to false by default until we check with user
bool pipeExists = false;
//initialize this arrays to NULL so anything that store in them gets cleared out.
//these arrays will hold the commands that the user wants to carry out.
char * command[1024] = {NULL, NULL, NULL};
char *command0[1024] = {NULL, NULL, NULL};
char *command1[] = {NULL, NULL, NULL};
char *command2[] = {NULL, NULL, NULL};
char *command3[] = {NULL, NULL, NULL};
char ** my_commands[] = {
command0,
command1,
command2,
command3,
NULL
};
//Important to delete mark the last byte as 0 in our input
buf[strlen(buf) -1] = 0;
//initialize this number to zero to start save the tokens at this index
int index = 0;
//a char * to hold the token saved by strtok
char * ptr;
ptr = strtok(buf, " \"");
//Loop through 'buf' and save tokens accordingly
while(ptr != NULL){
// if the user types exit at any moment, the program will exit gracefully and terminate
if(strcmp( ptr, "exit" ) == 0){
exit(0);
}
//if ptr is equal to | user wants to pipe something and we change pipeExists to true
if(strcmp( ptr, "|" ) == 0){
pipeExists = true;
index= 0;
ptr = strtok(NULL, " ");
}
//enter here while user doesnt want to user pipes
if(!pipeExists){
command[index] = ptr;
ptr = strtok(NULL, " ");
index++;
}
//enter here if user want to use pipes
if(pipeExists){
command0[index] = ptr;
ptr = strtok(NULL, " ");
index++;
}
g++;
printf("%s %i\n", ptr, g);
}
for (int s = 0; my_commands[s] != NULL; s++) {
cout << command0[s] << " \n" << endl;
}
//if pipes exists then initialize it
if(pipeExists){
pipe(pipeA);
}
//create first child
if ((pid[0] = fork()) == 0) {
//pass in the pipe, commands and pipe to function to execute
first_command(pipeA, command, pipeExists);
}
else if(pid[0] < 0){
//error with child
cerr<<"error forking first child"<<endl;
}
// if pipe exists create a second process to execute the second part of the command
if(pipeExists){
for(int f = 0; my_commands[f] != NULL; f++) {
//create second child
if ((pid[f] = fork()) == 0) {
other_command(pipeA, command0, index);
}
else if(pid[f] < 0){
//error with second child
cerr<<"error forking child "<< pid_num << endl;
}
}
pid_num++;
}
//if the pipe was created then we close its ends
if(pipeExists){
for(int z = 0; z < pid_num; z++) {
close(pipeA[z]);
}
}
//wait for the first child that ALWAYS executes
if ( (pid[0] = waitpid(pid[0], &status, 0)) < 0)
cerr<<"error waiting for first child"<<endl;
//wait for the second child but only if user wanted to created to use piping
if(pipeExists){
for(int j = 1; j < pid_num; j++) {
if ( (pid[j] = waitpid(pid[j], &status2, 0)) < 0){
printf("Status: %d", pid[j]);
cerr<<"error waiting for child " << j <<endl;
}
}
}
pid_num = 1;
}//endwhile
}
Is there any way to hide user input when asked for in C?
For example:
char *str = malloc(sizeof(char *));
printf("Enter something: ");
scanf("%s", str);getchar();
printf("\nYou entered: %s", str);
// This program would show you what you were writing something as you wrote it.
// Is there any way to stop that?
Another thing, is how can you only allow certain characters?
For example:
char c;
printf("Yes or No? (y/n): ");
scanf("%c", &c);getchar();
printf("\nYou entered: %c", c);
// No matter what the user inputs, it will show up, can you restrict that only
// showing up if y or n are entered?
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL)
int set_disp_mode(int fd,int option)
{
int err;
struct termios term;
if(tcgetattr(fd,&term)==-1){
perror("Cannot get the attribution of the terminal");
return 1;
}
if(option)
term.c_lflag|=ECHOFLAGS;
else
term.c_lflag &=~ECHOFLAGS;
err=tcsetattr(fd,TCSAFLUSH,&term);
if(err==-1 && err==EINTR){
perror("Cannot set the attribution of the terminal");
return 1;
}
return 0;
}
int getpasswd(char* passwd, int size)
{
int c;
int n = 0;
printf("Please Input password:");
do{
c=getchar();
if (c != '\n'||c!='\r'){
passwd[n++] = c;
}
}while(c != '\n' && c !='\r' && n < (size - 1));
passwd[n] = '\0';
return n;
}
int main()
{
char *p,passwd[20],name[20];
printf("Please Input name:");
scanf("%s",name);
getchar();
set_disp_mode(STDIN_FILENO,0);
getpasswd(passwd, sizeof(passwd));
p=passwd;
while(*p!='\n')
p++;
*p='\0';
printf("\nYour name is: %s",name);
printf("\nYour passwd is: %s\n", passwd);
printf("Press any key continue ...\n");
set_disp_mode(STDIN_FILENO,1);
getchar();
return 0;
}
for linux
For the sake of completeness: There is no way to do this in C. (That is, standard, plain C without any platform-specific libraries or extensions.)
You did not state why you wanted to do this (or on what platform), so it's hard to make relevant suggestions. You could try a console UI library or a GUI library. You could also try your platform's console libraries. (Windows, Linux)
I have been working on this project for a while. The purpose is to make a functioning shell that can do pretty much all the shell commands (except cd). It does almost everything I want it to do, except for a couple things. The first is that when I put an '&' to signify background processing, it does it, but then doesn't print another myshell> line. I can still input something, but the myshell> never shows up, no matter where I put another cout<<"myshell> ";.
Another issue is if I press enter, making myString empty, many times, it crashes the program with a seg fault. Also after I do the '&' background processing and press enter to get the myshell> to come back up, it prints one myshell> but then seg faults on the next hit of enter. I'm sorry if I didn't explain this well, but it is really driving me crazy. Please let me know if you have any suggestions.
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <cstdio>
#include <sys/wait.h>
#include <stdio.h>
/*Function that parses the command the user inputs.
It takes myArgv and myString as inputs.
It returns the value of exitcond, which is used to see if the user wants to exit or not.
Also, this is where myString is tokenized using strok()*/
int parseCommand(char *myArgv[10], char myString[255])
{
int exitcond=0;
if((strcmp(myArgv[0], "exit") == 0)||(strcmp(myArgv[0], "quit")==0))
{
exitcond = 1;
return exitcond;
}
int i;
char *token;
token = strtok(myString," ");
i=0;
while (token != NULL)
{
myArgv[i] = token;
token = strtok(NULL," ");
i++;
}
/*
* Set the last entry our new argv to a null char
* (see man execvp to understand why).
*/
myArgv[i] = '\0';
return exitcond;
}
/*Function that gets the command from the user and sees if they want
background processing or not (presence of '&').
It takes inputs of choose and myString. choose is the variable for
whether background processing is necessary or not, while myString is
an empty character array.
It outputs the value of the choose variable for lter use.*/
int getCommand(int choose, char myString[255])
{
int i;
choose=0;
fgets(myString, 256, stdin);
if (myString[0]=='\0')
{
choose=0;
return choose;
}
for (i=0; myString[i]; i++)
{
if (myString[i]== '&')
{
choose=1;
myString[i]=' ';
}
if (myString[i] == '\n')
{
myString[i] = '\0';
}
}
return choose;
}
/*Main function where all the calling of other functions and processes
is done. This is where the user enters and exits the shell also. All
usage of fork, pid, waitpid and execvp is done here.*/
int main()
{
using namespace std;
int exitCondition=0, i=0, status;
char myString[255];
char *token, *myArgv[10];
pid_t pid, waiting;
int bg=0;
while (!exitCondition)
{
/* print a prompt and allow the user to enter a stream of characters */
cout << "myshell> ";
bg=0;
int choose=0;
bg=getCommand(choose,myString);
exitCondition=parseCommand(myArgv,myString);
if(exitCondition==1)
{
cout<<"Thank you for using my shell.\n";
}
else {
/* while (myString[0]=='\0')
{
cout<<"myshell> ";
bg=getCommand(choose,myString);
}*/
/* The user has a command, so spawn it in a child process */
pid = fork();
if (pid == -1)
{
/* to understand why this is here, see man 2 fork */
cout << "A problem arose, the shell failed to spawn a child process" << endl;
return(1);
}
else if (pid == 0)
{
// Child process
execvp(myArgv[0],myArgv);
cout << "Bad command or file name, please try again!\n" << endl;
return 0;
} else {
/* This makes sure that the spawned process is run in the foreground,
because the user did not choose background */
if(bg==0)
{
waitpid(pid,NULL,0);
}
}
}
}
return 0;
}
Okay, you had three bugs, one of which caused the segfault. Of the others, one would put a garbage argument in the array passed to execvp and the other would leak zombie processes for background jobs.
I've corrected the code and annotated it with where the bugs were along with the fixes [please pardon the gratuitous style cleanup]:
#include <cstdio>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define AVCOUNT 100
#define STRBUFLEN 2000
/*Function that parses the command the user inputs.
It takes myArgv and myString as inputs.
It returns the value of exitcond, which is used to see if the user wants to
exit or not.
Also, this is where myString is tokenized using strok()*/
int
parseCommand(char **myArgv, char *myString)
{
char *token;
char *bp;
int exitcond = 0;
int i;
// NOTE/BUG: original check for exit/quit was here -- at this point
// myArgv is undefined (hence the segfault)
// NOTE/BUG: your original loop -- at the end i was one beyond where it
// should have been so that when myArgv gets passed to execvp it would
// have an undefined value at the end
#if 0
token = strtok(myString, " ");
i = 0;
while (token != NULL) {
myArgv[i] = token;
token = strtok(NULL, " ");
i++;
}
#endif
// NOTE/BUGFIX: here is the corrected loop
i = 0;
bp = myString;
while (1) {
token = strtok(bp, " ");
bp = NULL;
if (token == NULL)
break;
myArgv[i++] = token;
}
/*
* Set the last entry our new argv to a null pointer
* (see man execvp to understand why).
*/
// NOTE/BUG: with your code, i was one too high here
myArgv[i] = NULL;
// NOTE/BUGFIX: moved exit/quit check to here now that myArgv is valid
token = myArgv[0];
if (token != NULL) {
if ((strcmp(token, "exit") == 0) || (strcmp(token, "quit") == 0))
exitcond = 1;
}
return exitcond;
}
/*Function that gets the command from the user and sees if they want
background processing or not (presence of '&').
It takes inputs of choose and myString. choose is the variable for
whether background processing is necessary or not, while myString is
an empty character array.
It outputs the value of the choose variable for lter use.*/
int
getCommand(int choose, char *myString)
{
int i;
choose = 0;
fgets(myString, STRBUFLEN, stdin);
if (myString[0] == '\0') {
choose = 0;
return choose;
}
for (i = 0; myString[i]; i++) {
if (myString[i] == '&') {
choose = 1;
myString[i] = ' ';
}
if (myString[i] == '\n') {
myString[i] = '\0';
break;
}
}
return choose;
}
/*Main function where all the calling of other functions and processes
is done. This is where the user enters and exits the shell also. All
usage of fork, pid, waitpid and execvp is done here.*/
int
main()
{
using namespace std;
int exitCondition = 0;
int status;
char myString[STRBUFLEN];
char *myArgv[AVCOUNT];
pid_t pid;
int bg = 0;
while (!exitCondition) {
// NOTE/BUGFIX: without this, any background process that completed
// would become a zombie because it was never waited for [again]
// reap any finished background jobs
while (1) {
pid = waitpid(0,&status,WNOHANG);
if (pid < 0)
break;
}
/* print a prompt and allow the user to enter a stream of characters */
cout << "myshell> ";
bg = 0;
int choose = 0;
bg = getCommand(choose, myString);
exitCondition = parseCommand(myArgv, myString);
if (exitCondition == 1) {
cout << "Thank you for using my shell.\n";
break;
}
/* while (myString[0]=='\0') { cout<<"myshell> "; bg=getCommand(choose,myString); } */
/* The user has a command, so spawn it in a child process */
pid = fork();
if (pid == -1) {
/* to understand why this is here, see man 2 fork */
cout << "A problem arose, the shell failed to spawn a child process" << endl;
return 1;
}
if (pid == 0) {
// Child process
execvp(myArgv[0], myArgv);
cout << "Bad command or file name, please try again!\n" << endl;
return 1;
}
/* This makes sure that the spawned process is run in the
foreground, because the user did not choose background */
if (bg == 0)
waitpid(pid, &status, 0);
}
return 0;
}
With the help of many people here, I've been writing a program that writes the contents of the Windows clipboard to a text file. (I'm working in Visual Studio 2010.) I've been trying to work out the logic of a for loop that will test the command-line arguments (if any); the arguments can be
a codepage number
a filename or path
or both, in any order. If no codepage is specified (or if the user specifies an invalid codepage), the program uses the default Windows codepage (typically 1252). If no filename is specified, the program writes the output to "#clip.txt".
I know my method of reading the arguments is inefficient, but it's the best I can figure out right now. I use two for loops. The first checks each command-line parameter; if the string is NOT all-digits, it uses the string as a filename and then breaks. The next loop again checks each parameter, and if the string is all-digits, it assigns it as the codepage number and then breaks.
The idea is that if the user enters
clipwrite 500 850
only the first (500) should get used as the codepage. And if the user enters
clipwrite foo.txt bar.txt
the output should be written to foo.txt.
My code seems to work correctly if the user enters no arguments, one argument only, or one number and one alpha string. But I'm clearly doing something wrong, because if the user enters
clipwrite 500 850
then 850 gets used (it should be ignored). And if the user enters
clipwrite foo.txt bar.txt
the program crashes. Can anyone help me sort what's wrong with my logic? Here's the relevant code (which uses a command-line parsing routine to get argc and argv):
if (argc > 1) {
// get name of output file if specified
for ( i = 1; i < argc; i++ ) {
if (i < 3) {
string argstr = argv[i];
//if string is not digits-only, use as filename
for (size_t n = 0; n <argstr.length(); n++) {
if (!isdigit( argstr[ n ]) ) {
OutFile = argv[i];
break;
}
}
}
}
// get codepage number if specified
for ( i = 1; i < argc; i++ ) {
if (i < 3) {
string argstr = argv[i];
for (size_t n = 0; n <argstr.length(); n++) {
if (!isdigit( argstr[ n ]) ) {
// if all chars are digits
} else {
// convert codepage string to integer
int cpint = atoi(argstr.c_str());
// check if codepage is valid; if so use it
if (IsValidCodePage(cpint)) {
codepage = "."+argstr;
}
break;
}
}
}
}
}
Many thanks for any help with this beginner-level problem.
Maybe something like this:
#include <iostream>
#include <string>
#include <cstring>
#include <cctype>
#include <cstdlib>
int main(int, char **argv) {
std::string filename = "#clip.txt";
int codepage = 1252;
bool bFilenameSet = false;
bool bCodepageSet = false;
for (++argv; *argv; ++argv) { // *argv == NULL at end of arguments
char *p = *argv;
for ( ; *p; ++p)
if (!isdigit(*p))
break;
if (*p) { // non-digit found
if (!bFilenameSet) {
filename = *argv;
bFilenameSet = true;
}
}
else {
if (!bCodepageSet) {
codepage = atoi(*argv);
bCodepageSet = true;
}
}
}
std::cout<< "Filename: "<< filename<< "\n";
std::cout<< "Codepage: "<< codepage<< "\n";
return 0;
}
I ran your program but it wasn't complete, so I assumed that isValidCodePage() function always returns true.
What I can see from your code is that you are overwriting codepage and Outfile because you are only breaking the inner loop, see this article for an explaination of the break statement
I don't see any immediate reason for a crash, but:
When issuing clipwrite 500 850 you use the codepage 850 since your break; only leaves the inner loop but your code keeps iterating over
the arguments and your codepage variable gets overwritten.
Your usage of isdigit is faulty. Whenever a string starts with a digit you try to interpret it as an integer even if its 1bla.txt.
atoi() is evil since it fails to report if a given string can't be parsed as a number. Better use std::stringstream and >> operator.
May be you should do it like this:
int cpint = -1;
std::string fname="";
for ( int i = 1; i < argc && i<3; i++ ) {
std::stringstream argss(argv[i]);
// Check if the string is a decimal
// and only a decimal
if( !(argss >> cpint) || !argss.eof()) {
fname=argv[i];
}
}
if(!fname.empty())
std::cerr << "filename '" << fname "'" << std::endl;
if(cpint!=-1)
std::cerr << "codepage: #" << cpint << std::endl;
Not really tested but I hope you get the idea
Using the answers here I finally got everything working, though I know my code is still inefficient. Here is the VS2010 source code that I used for this clipboard writing utility. Thanks to all who responded.
// ClipWrite.cpp
#include "stdafx.h"
#include <Windows.h>
#include <shellapi.h>
#include <iostream>
#include <fstream>
#include <codecvt> // for wstring_convert
#include <locale> // for codecvt_byname
#include <sstream>
using namespace std;
// helper gets path to this application
string ExePath() {
char buffer[MAX_PATH];
GetModuleFileNameA( NULL, buffer, MAX_PATH );
string::size_type pos = string( buffer ).find_last_of( "\\/" );
return string( buffer ).substr( 0, pos);
//return std::string( buffer ).substr( 0, pos);
}
// set variable for command-line arguments
char **argv = NULL;
// helper to get command-line arguments
int ParseCommandLine() {
int argc, BuffSize, i;
WCHAR *wcCommandLine;
LPWSTR *argw;
wcCommandLine = GetCommandLineW();
argw = CommandLineToArgvW( wcCommandLine, &argc);
argv = (char **)GlobalAlloc( LPTR, argc + 1);
for( i=0; i < argc; i++) {
BuffSize = WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK, argw[i], -1, NULL, 0, NULL, NULL );
argv[i] = (char *)GlobalAlloc( LPTR, BuffSize );
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK, argw[i], BuffSize * sizeof( WCHAR ),argv[i], BuffSize, NULL, NULL );
}
return argc;
}
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{
// for logging in case of error
int writelog = 0;
string logtext = "";
// create output filename
string filename = ExePath() + "\\#clip.txt";
// get default codepage from Windows, typically 1252
int iCP=GetACP();
string sCP;
ostringstream convert;
convert << iCP;
sCP = convert.str();
// construct string to use for conversion routines (e.g. ".1252")
string sDefaultCP = "."+sCP;
string sOutputCP = "."+sCP;
// read command line for alternate codepage and/or filename
int i, argc;
argc = ParseCommandLine( );
if (argc > 1) {
bool bFilenameSet = false;
bool bCodepageSet = false;
int cpint = -1;
for ( i = 1; i < argc && i<3; i++ ) {
std::string argstr = argv[i];
//if string has only digits, use as codepage;
for (size_t n = 0; n <argstr.length(); n++) {
if (!isdigit( argstr[ n ]) ) {
if (!bFilenameSet) {
filename = argv[i];
bFilenameSet = true;
}
} else {
// convert codepage string to integer
if (!bCodepageSet) {
std::stringstream argss(argv[i]);
if( (argss >> cpint) || !argss.eof()) {
argstr = argv[i];
logtext = logtext + "Requested codepage (if any): " + argstr + "\n";
cout << "Requested codepage (if any): " << argstr << endl;
// check if codepage is valid; if so, use it
if (IsValidCodePage(cpint)) {
sCP = argstr;
sOutputCP = "."+argstr;
}
bCodepageSet = true;
}
}
}
}
}
}
cout << "Codepage used: " + sCP << endl;
// get clipboard text
string cliptext = "";
if (OpenClipboard(NULL)) {
if(IsClipboardFormatAvailable(CF_TEXT)) {
HGLOBAL hglb = GetClipboardData(CF_TEXT);
if (hglb != NULL) {
LPSTR lptstr = (LPSTR)GlobalLock(hglb);
if (lptstr != NULL) {
// read the contents of lptstr
cliptext = (char*)hglb;
// release the lock
GlobalUnlock(hglb);
}
}
}
CloseClipboard();
}
// create conversion routines
typedef std::codecvt_byname<wchar_t,char,std::mbstate_t> codecvt;
std::wstring_convert<codecvt> cp1252(new codecvt(sDefaultCP));
std::wstring_convert<codecvt> outpage(new codecvt(sOutputCP));
ofstream OutStream; // open an output stream
OutStream.open(filename, std::ios_base::binary | ios::out | ios::trunc);
// make sure file is successfully opened
if(!OutStream) {
writelog = 1;
logtext = logtext + "Error opening file " + filename + " for writing.\n";
//return 1;
} else {
// convert to DOS/Win codepage number in "outpage"
OutStream << outpage.to_bytes(cp1252.from_bytes(cliptext)).c_str();
OutStream.close(); // close output stream
if (writelog == 1) {
logtext = logtext + "Output file: " + filename + "\n";
}
}
if (writelog == 1) {
logtext = logtext + "Codepage used: " + sCP + "\n";
string LogFile = ExePath() + "\\#log.txt";
ofstream LogStream;
LogStream.open(LogFile, ios::out | ios::trunc);
if(!LogStream) {
cout << "Error opening file " << LogFile << " for writing.\n";
return 1;
}
LogStream << logtext;
LogStream.close(); // close output stream
}
return 0;
}
I working my way through a C++ and Operating Systems book and I've come upon an assignment that requires creation, writing, and reading from pipes. However my program stalls on reading from the second pipe. My program is to accept input and parse out a space delimited string into tokens and classifying those tokens accordingly. My code is bellow with my problem area marked. Any help is as always very appreciated.
edit: This is supposed to have two children. One for processing the space delimited tokens and the other for determining the type of delimited tokens. As far as debugging goes I only have access to cout as a debugger. So I inserted a cout before the read and after the one before the read appeared but the one after did not.
#include <iostream>
#include <fstream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
//declaring the pipes
int pipeOne[2];
int pipeTwo[2];
struct inputStruct {
char str[256]; /* one extra spot for \n */
int len; /* length of str */
int flag; /* 0 for normal input, 1 to indicate “done” */
};
struct tokenStruct {
char token[256]; /* tokens can be 255 max */
int flag; /* same as inputStruct */
int tokenType; /* a code value */
};
void dataProcess(){
//new input struct to contain the the input from the parent
inputStruct input;
//the intial read from the pipe to populate the input stuct
read( pipeOne[0], (char*)&input, sizeof(inputStruct));
//set the flag
int flag = input.flag;
while (flag != 1){
int size = 0;
//get the size of the array up until the null character
while (input.str[size] != '\0'){
size++;
}
//Here's the parsing of each token
for (int i=0; i<size; i++) {
int tokenLength;
tokenStruct token;
//while the char isn't white space or null increment through it
while (input.str[i] != ' ' && input.str[i] != '\0') {
//a is the index of the string token
int a = 0;
//write the parsed string
token.token[a] = input.str[i];
a++;
i++;
}
//write to process 2
write(pipeTwo[1], (char*)&token, sizeof(tokenStruct));
}
//read again and store the results
read(pipeOne[0], (char*)&input, sizeof(inputStruct));
flag = input.flag;
}
tokenStruct token;
token.flag = flag;
//final write to the second child to tell it to commit suicide
write(pipeTwo[1], (char*)&token, sizeof(tokenStruct));
exit(0);
}
void tokenClassifer(){
tokenStruct token;
//Problem area is here on ****************************************************
//the initial read
read(pipeTwo[0], (char*)&token, sizeof(tokenStruct));
while (token.flag != 1){
int size = 0;
//get the size of the array up until the null character
while (token.token[size] != '\0'){
size++;
}
if (size == 1) {
//check for the one char things first
switch (token.token[0])
{
case '(':
token.tokenType = 0;
break;
case ')':
token.tokenType = 0;
break;
case ';':
token.tokenType = 0;
break;
case '+':
token.tokenType = 1;
break;
case '-':
token.tokenType = 1;
break;
case '/':
token.tokenType = 1;
break;
case '*':
token.tokenType = 1;
break;
default:
if (isdigit(token.token[0])) {
token.tokenType = 2;
} else {
token.tokenType = 3;
}
break;
}
} else {
bool isStr;
int i = 0;
//check for the more than one character
while (token.token[i] != '\0'){
//check if it's a string or digits
if (isdigit(token.token[0])) {
isStr=false;
} else{
//set up the bools to show it is a string
isStr=true;
break;
}
}
//if it is a string token type 3
if (isStr) {
token.tokenType = 3;
} else {
//if not then it's digits and token type 2
token.tokenType = 2;
}
}
//print out the token and token type
cout << "Token type is: " << token.tokenType << "Token value is: " << token.token << "\n";
//read the pipe again and start the process all over
read(pipeTwo[0], (char*)&token, sizeof(tokenStruct));
}
exit(0);
}
int main()
{
//create the pipes for reading and writing between processes
pipe(pipeOne);
pipe(pipeTwo);
//fork off both processes
int value = fork();
int value2 = fork();
//do the process for the first fork
if(value == 0){
//fork one
dataProcess();
} else {
wait(0);
}
//do the process for the second fork
if (value2 == 0) {
//fork two
//the token classifer function for the second fork
tokenClassifer();
} else {
cout << "Type some tokens (or just press enter to quit) \n";
//this is all of the parent functions
for (string line; getline(cin, line); )
{
inputStruct input;
if (line.empty())
{
// if the line is empty, that means the user didn't
// press anything before hitting the enter key
input.flag = 1;
write( pipeOne[1], (char*)&input, sizeof(inputStruct));
break;
} else {
//else copy the string into an array
strcpy(input.str, line.c_str());
//set the flag to zero to show everthing is ok
input.flag = 0;
}
//write the stuct to the pipe
write( pipeOne[1], (char*)&input, sizeof(inputStruct));
cout << "Type some tokens (or just press enter to quit) \n";
}
wait(0);
}
}
One problem that is evident:
//fork off both processes
int value = fork();
int value2 = fork();
This will fork 3 new processes. The initial fork will leave you with two processes, each of which go on to fork a new process.
EDIT:
Proper forking:
int value = fork();
if (value == 0) {
// do child stuff
exit(0);
} else if (value == -1) {
//fork failed
}
int value2 = fork();
if (value2 == 0) {
//do child stuff
exit(0);
} else if (value2 == -1) {
//fork failed
}
I'm actually not quite clear about how data goes through your program, so I'll leave it to you to add the waits. I'd actually change the names of value and value2, but that's just me. Also, I'm only addressing the forking issue here so there may be other problems with your code (which I kind of suspect since you have two pipes).
EDIT 2:
Another issue that I see is that you're not closing the ends of the pipes that you don't use. If you never close the write end of a pipe, your reads will block until the pipe has data (or there are no more writers to the pipe, that is, the write end is not open). This means that the write end of the pipe should be closed in all processes when you are not using it or are finished with it.