fgets and chdir acting strangely together in C - c

I am currently creating a simple shell for homework and I've run into a problem. Here is a snippet of code with the pieces that pertain to the problem (I may have forgotten some pieces please tell me if you see anything missing):
eatWrd returns the first word from a string, and takes that word out of the string.
wrdCount, as implied, returns the number of words in a string.
if either of these codes are necessary for a response I can post them, just please tell me, I am almost 100% positive they are not the cause of the problem.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100
int main(void)
{
char input[MAX];
char *argm[MAX];
memset(input, 0, sizeof(input));
memset(argm, 0, sizeof(argm));
while(1){
printf("cmd:\n");
fgets(input, MAX-1, stdin);
for(i=0;i < wrdCount(input); i++){
argm[i] = eatWrd(input);
}
argm[i] = NULL;
if (!strncmp(argm[0],"cd" , 2)){
chdir(argm[1]);
}
if (!strncmp(argm[0],"exit", 4)){
exit(0);
}
memset(input, 0, sizeof(input));
memset(argm, 0, sizeof(argm));
}
}
Anyways, this loop works for lots of other commands using execvp, (such as cat, ls, etc.), when I use cd, it works as expected, except when I try to exit the shell, it takes multiple exit calls to actually get out. (as it turns out, the number of exit calls is exactly equal to the number of times I call cd). It only takes one exit call when I don't use cd during a session. I'm not really sure what's going on, any help is appreciated, thanks.
Here is eatWrd:
char* eatWrd(char * cmd)
{
int i = 0; // i keeps track of position in cmd
int count = 0; // count keeps track of position of second word
char rest[MAX_LINE]; // rest will hold cmd without the first word
char * word = (char *) malloc(MAX_LINE); //word will hold the first word
sscanf(cmd, "%s", word); //scan the first word into word
// iterate through white spaces, then first word, then the following white spaces
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
}
while(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n' && cmd[i] != '\0'){
i++;
count++;
}
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
}
// copy the rest of cmd into rest
while(cmd[i] != '\n' && cmd[i] != '\0'){
rest[i-count] = cmd[i];
i++;
}
rest[i-count] = '\0';
memset(cmd, 0, MAX_LINE);
strcpy(cmd, rest); //move rest into cmd
return word; //return word
}
And here is wrdCount:
int wrdCount(char *sent)
{
char *i = sent;
int words = 0;
//keep iterating through the string,
//increasing the count if a word and white spaces are passed,
// until the string is finished.
while(1){
while(*i == ' ' || *i == '\t') i++;
if(*i == '\n' || *i == '\0') break;
words++;
while(*i != ' ' && *i != '\t' && *i != '\n' && *i != '\0') i++;
}
return words;
}

This variation on your code works for me:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define MAX 100
char *eatWrd(char **line) {
char *next_c = *line;
char *word_start = NULL;
while (isspace(*next_c)) next_c += 1;
if (*next_c) {
word_start = next_c;
do {
next_c += 1;
} while (*next_c && ! isspace(*next_c));
*next_c = '\0';
*line = next_c + 1;
}
return word_start;
}
int main(void)
{
char input[MAX];
char *argm[MAX];
while(1) {
int word_count = 0;
char *next_input = input;
printf("cmd:\n");
fgets(input, MAX, stdin);
do {
argm[word_count] = eatWrd(&next_input);
} while (argm[word_count++]);
/* The above always overcounts by one */
word_count -= 1;
if (!strcmp(argm[0], "cd")){
chdir(argm[1]);
} else if (!strcmp(argm[0], "exit")) {
exit(0);
}
}
}
Note my variation on eatWrd(), which does not have to move any data around, and which does not require pre-parsing the string to determine how many words to expect. I suppose your implementation would be more complex, so as to handle quoting or some such, but it could absolutely follow the same general approach.
Note, too, my correction to the command-matching conditions, using !strcmp() instead of strncmp().

Related

Make 1st letter the last letter in the word

I need help rotating words so they output with the 1st letter at the end.
I have a file called flip.txt
backpack
carpet
rotate
and i want to be able to enter ./RotateWord < flip.txt (in terminal) and it should output
ackpackb
arpetc
otater
I was able to get it to output the words with the 1st letter missing . How to i make it output the 1st letter at the end?
Heres my code
#include <stdio.h>
#define BUFFER_SIZE 81
int main(int argc, char **argv) {
char string[BUFFER_SIZE];
while(fgets(string, BUFFER_SIZE, stdin) > 0) {
int numChars = 0;
while(string[numChars] && string[numChars] != '\n')
++numChars;
int i;
for(i = 1; i < numChars; ++i){
if (string[i] == '\n'){
putchar(string[0]);
putchar('\n');
}
else {
putchar(string[i]);
}
}
putchar('\n');
fflush(stdout);
}
return 0;
}
You can use strcspn to get rid of the \n.
string[strcspn(string,"\n")]=0;
If you want to print the string in that way - do this two line. (You can merge it in one line too).
printf("%s",string+1);
printf("%c\n",string[0]);
The code will be
while(fgets(string, BUFFER_SIZE, stdin) != NULL) {
string[strcspn(string,"\n")]=0;
printf("%s%c\n",string+1,string[0]);
fflush(stdout);
}
Why your code didn't work?
The thing is as per your code the if block is never executed. So you will never get the first character printed. The previous while loop simply increments the variable as long as it doesn't meet the \n. Then in the next loop you iterate over them < numChars and then expect to see the \n, which won't be the case now.
Changing your code this would work
while(fgets(string, BUFFER_SIZE, stdin) != NULL) {
for(int i = 1; string[i]; ++i){
if (string[i] == '\n'){
putchar(string[0]);
putchar('\n');
}
else {
putchar(string[i]);
}
}
fflush(stdout);
}
Earlier you were putting \n after every character that you print. It should be only after the last character.
Note:
fgets returns char* - in case of error it returns NULL. So to check if fgets is successful or not you should do a null check.
It it possible that you're not counting the final '\n' character when you
while(string[numChars] && string[numChars] != '\n')
++numChars;
?
If so, you never hit
if (string[i] == '\n'){
putchar(string[0]);
putchar('\n');
}
because your loop stops one early.
In the for loop run till
for(i=1,i<=numChars,i++)
This is because the if statement does not get executed as the index does not reach the newline character.
Thus the complete code is as follows:
#include <stdio.h>
#define BUFFER_SIZE 81
int main(int argc, char **argv) {
char string[BUFFER_SIZE];
while(fgets(string, BUFFER_SIZE, stdin) > 0) {
int numChars = 0;
while(string[numChars] && string[numChars] != '\n')
++numChars;
int i;
for(i = 1; i <= numChars; ++i){
if (string[i] == '\n'){
putchar(string[0]);
putchar('\n');
}
else {
putchar(string[i]);
}
}
//putchar('\n');
fflush(stdout);
}
return 0;
}
Also there is no need of extra
putchar('\n');

tempWord[0]='\0' Does not reset String somehow

I wrote a program in C, The expected result should be:
$ cat poem.txt
Said Hamlet to Ophelia,
I'll draw a sketch of thee,
What kind of pencil shall I use?
2B or not 2B?
$ ./censor Ophelia < poem.txt
Said Hamlet to CENSORED,
I'll draw a sketch of thee,
What kind of pencil shall I use?
2B or not 2B?
But I got this:
$ ./censor Ophelia < poem.txt
Said Hamlet tomlet CENSORED,
I'lllia drawlia arawlia sketcha ofetcha theecha,
Whatcha kindcha ofndcha pencila shallla Ihallla usellla?
2Bsellla orellla notllla 2Botllla?
I use tempWord to store every word and compare it with the word that needs to be censored. Then I use tempWord[0]='\0' to reset the temp String, so that I can do another comparison. But it seems not working. Can anyone help?
# include <stdio.h>
# include <string.h>
int compareWord(char *list1, char *list2);
int printWord(char *list);
int main(int argc, char *argv[]) {
int character = 0;
char tempWord[128];
int count = 0;
while (character != EOF) {
character = getchar();
if ((character <= 'z' && character >= 'a') ||
(character <= 'Z' && character >= 'A') ||
character == 39) {
tempWord[count] = character;
count++;
} else {
if (count != 0 && compareWord(tempWord, argv[1])) {
printf("CENSORED");
count = 0;
tempWord[0] = '\0';
}
if (count != 0 && !compareWord(tempWord, argv[1])) {
printWord(tempWord);
count = 0;
tempWord[0] = '\0';
}
if (count == 0) {
printf("%c", character);
}
}
}
return 0;
}
int printWord(char *list) {
// print function
}
int compareWord(char *list1, char *list2) {
// compareWord function
}
There are multiple issues in your code:
You do not test for end of file at the right spot: if getc() returns EOF, you should exit the loop immediately instead of processing EOF and exiting at the next iteration. The classic C idiom to do this is:
while ((character = getchar()) != EOF) {
...
For portability and readability, you should use isalpha() from <ctype.h> to check if the byte is a letter and avoid hardcoding the value of the value of the apostrophe as 39, use '\'' instead.
You have a potential buffer overflow when storing the bytes into the tempWord array. You should compare the offset with the buffer size.
You do not null terminate tempWord, hence the compareWord() function cannot determine the length of the first string. The behavior is undefined.
You do not check if a command line argument was provided.
The second test is redundant: you could just use an else clause.
You have undefined behavior when printing the contents of tempWord[] because of the lack of null termination. This explains the unexpected behavior, but you might have much worse consequences.
printWord just prints a C string, use fputs().
The compWord function is essentially the same as strcmp(a, b) == 0.
Here is a simplified and corrected version:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char tempWord[128];
size_t count = 0;
int c;
while ((c = getchar()) != EOF) {
if (isalpha(c) || c == '\'') {
if (count < sizeof(tempWord) - 1) {
tempWord[count++] = c;
}
} else {
tempWord[count] = '\0';
if (argc > 1 && strcmp(tempWord, argv[1]) == 0) {
printf("CENSORED");
} else {
fputs(tempWord, stdout);
}
count = 0;
putchar(c);
}
}
return 0;
}
EDIT: chux rightfully commented that the above code does not handle 2 special cases:
words that are too long are truncated in the output.
the last word is omitted if it falls exactly at the end of file.
I also realized the program does not handle the case of long words passed on the command line.
Here is a different approach without a buffer that fixes these shortcomings:
#include <ctype.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
const char *word = (argc > 1) ? argv[1] : "";
int count = 0;
int c;
for (;;) {
c = getchar();
if (isalpha(c) || c == '\'') {
if (count >= 0 && (unsigned char)word[count] == c) {
count++;
} else {
if (count > 0) {
printf("%.*s", count, word);
}
count = -1;
putchar(c);
}
} else {
if (count > 0) {
if (word[count] == '\0') {
printf("CENSORED");
} else {
printf("%.*s", count, word);
}
}
if (c == EOF)
break;
count = 0;
putchar(c);
}
}
return 0;
}
tempWord[0] = '\0';
It will not reset the variable to null. It just assign the '\0' to the first position. But The values which are assigned are still in memory only. Only the first position is assigned to '\0'. So, to reset the character array try the below.
memset(tempWord, 0, 128);
Add the above line instead of your tempWord[0] = '\0'.
And also this will solves you don't need to add the '\0' at end of each word. This itself will work. But for the first time your have to reset the character array using the same memset function. Before entering to the loop you have to set the tempWord to null using the memset function.
Using tempWord[0]='\0' will not reset the whole array, just the first element. Looking at your code, there are 2 ways you could go forward, either reset the whole array by using memset:
memset(tempWord, 0, sizeof tempWord);
or
memset(tempWord, 0, 128);
(or you can only clear it by the size of last word, also it needs string.h which you have already included),
Or you could just set the element after the length of 'current word' to be '\0' (ex, if current word is the then set tempWord[3]='\0', since strlen checks the string till null char only) which can be placed before those 2 ifs checking if the strings are equal or not, your new while loop will look like this:
{
character = getchar();
if((character<='z' && character>='a')||(character<='Z' && character>='A')||character == 39)
{
tempWord[count]=character;
count++;
}else {
tempWord[count]='\0';
if(count!=0 && compareWord(tempWord, argv[1]))
{
printf("CENSORED");
count=0;
}
if(count!=0 && !compareWord(tempWord, argv[1]))
{
printWord(tempWord);
count=0;
}
if (count==0)
{
printf("%c", character);
}
}
}
(it works, tested)

C program to count total words in an input file

Input file contains a completely empty line at line 2 and an unnecessary white space after the final full stop of the text. With this input file I am getting 48 words while I was suppose to get 46 words.
My input file contains:
"Opening from A Tale of Two Cities by Charles Darwin
It was the best of times, it was the worst of times. It was the age
of wisdom, it was the age of foolishness. It was the epoch of
belief, it was the epoch of incredulity. "
Here's how I tried:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define max_story_words 1000
#define max_word_length 80
int main (int argc, char **argv)
{
char story[max_story_words][max_word_length] = {{0}};
char line[max_story_words] = {0};
char *p;
char ch = 0;
char *punct="\n ,!.:;?-";
int num_words = 1;
int i = 0;
FILE *file_story = fopen ("TwoCitiesStory.txt", "r");
if (file_story==NULL) {
printf("Unable to open story file '%s'\n","TwoCitiesStory.txt");
return (EXIT_FAILURE);
}
/* count words */
while ((ch = fgetc (file_story)) != EOF) {
if (ch == ' ' || ch == '\n')
num_words++;
}
rewind (file_story);
i = 0;
/* read each line in file */
while (fgets (line, max_word_length, file_story) != NULL)
{
/* tokenize line into words removing punctuation chars in punct */
for (p = strtok (line, punct); p != NULL; p = strtok (NULL, punct))
{
/* convert each char in p to lower-case with tolower */
char *c = p;
for (; *c; c++)
*c = tolower (*c);
/* copy token (word) to story[i] */
strncpy ((char *)story[i], p, strlen (p));
i++;
}
}
/* output array */
for(i = 0; i < num_words; i++)
printf ("story[%d]: %s\n", i, story[i]);
printf("\ntotal words: %d\n\n",num_words);
return (EXIT_SUCCESS);
}
Your num_words takes account of the two extra whitespaces, that's why you get 48.
You should simply print i immediately after the fgets-strtok loop, if I'm not mistaken.
Something along these lines:
while ((ch = fgetc (file_story)) != EOF) {
if (ch == ' ') {
num_words++;
while( (ch = fgetc (file_story)) == ' ' && (ch != EOF) )
}
if (ch == '\n') {
num_words++;
while( (ch = fgetc (file_story)) == '\n' && (ch != EOF) )
}
Though I wonder why you are only taking whitespace and newline characters for counting new words. Two words separated by some other punctuation mark are definitely not accouted for in your code
My suggestion is to change the words counting loop as follows:
/* count words */
num_words = 0;
int flag = 0; // set 1 when word starts and 0 when word ends
while ((ch = fgetc (file_story)) != EOF) {
if ( isalpha(ch) )
{
if( 0 == flag ) // if it is a first letter of word ...
{
num_words++; // ... add to word count
flag = 1; // and set flag to skip not first letters
}
continue;
}
if ( isspace(ch) || ispunct(ch) ) // if word separator ...
{
flag = 0; // ... reset flag
}
}

two C functions trying to return the first word in a string

I have created the two following functions. The first, eatWrd, returns the first word in a string without any white spaces, and removes the first word from the input string:
MAX is a number representing the max length of a string
char* eatWrd(char * cmd)
{
int i = 0; //i will hold my place in cmd
int count = 0; //count will hold the position of the second word
int fw = 0; //fw will hold the position of the first word
char rest[MAX]; // rest will hold cmd without the first word
char word[MAX]; // word will hold the first word
// start by removing initial white spaces
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
fw++;
}
// now start reading the first word until white spaces or terminating characters
while(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n' && cmd[i] != '\0'){
word[i-fw] = cmd[i];
i++;
count++;
}
word[i-fw] = '\0';
// now continue past white spaces after the first word
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
}
// finally save the rest of cmd
while(cmd[i] != '\n' && cmd[i] != '\0'){
rest[i-count] = cmd[i];
i++;
}
rest[i-count] = '\0';
// reset cmd, and copy rest back into it
memset(cmd, 0, MAX);
strcpy(cmd, rest);
// return word as a char *
char *ret = word;
return ret;
}
The second, frstWrd, just returns the first word without modifying the input string:
// this function is very similar to the first without modifying cmd
char* frstWrd(char * cmd)
{
int i = 0;
int fw = 0;
char word[MAX];
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
fw++;
}
while(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n' && cmd[i] != '\0'){
word[i-fw] = cmd[i];
i++;
}
word[i-fw] = '\0';
char *ret = word;
return ret;
}
To test the function, I used fgets to read a string from the User(me), and then I printed three strings (frstWrd(input), eatWrd(input), eatWrd(input)). I would have expected that given a string, "my name is tim" for example, the program would print "my my name", but instead it prints the third word three times over, "is is is":
// now simply test the functions
main()
{
char input[MAX];
fgets(input, MAX - 1, stdin);
printf("%s %s %s", frstWrd(input), eatWrd(input), eatWrd(input));
}
I have looked over my functions over and over and cannot see the mistake. I believe there is simply something I don't know about printf, or about using multiple string modification functions as arguments in another function. Any insight would be helpful thanks.
As I see rest and word are local variables in the function eatWrd. So it is bad practice to return pointer to such memory outside functions.
EDIT 1:
Also you should understand, that in line
printf("%s %s %s", frstWrd(input), eatWrd(input), eatWrd(input));
function eatWrd(input) could be called the first (before frstWrd(input)).
EDIT 2:
This can be usefull in finction eatWrd
//char rest[MAX]; // rest will hold cmd without the first word
char * rest = (char*) malloc(MAX);
And new main let be as:
int main()
{
char input[MAX];
fgets(input, MAX - 1, stdin);
printf("%s ", frstWrd(input));
printf("%s ", eatWrd(input));
printf("%s\n", eatWrd(input));
}
And in the end my solution for frstWrd (just to show how standard functions can be useful):
char* frstWrd(char * cmd)
{
char * word = (char *) malloc(MAX);
sscanf(cmd, "%s", word);
return word;
}

putc to print string to word in separate line C

i wanna print string to each word in each line .it looks like putc not working and why i can not use putchar in c programming language ?
#include <stdio.h>
#include <conio.h>
#include <string.h>
void main() {
char s[50];
int i, len;
printf("\enter string : ");
gets(s);
len = strlen(s);
i = 0;
while (i<len) {
while (s[i] == ' ' && i<len)
i++;
while (s[i] != ' ' && i<len)
putc(s[i++], stdout);
putc('\n', stdout);
}
getch();
}
There's nothing massively wrong with your code, other than:
the use of non-standard features like conio.h and getch;
the use of unsafe functions (which tend not to matter in classwork that much);
the wrong order for conditions in your while statements, which may cause undefined behaviour.
Hence, this code appears to do what you need:
#include <stdio.h>
#include <string.h>
int main (void) {
char s[50];
int i, len;
printf("enter string : ");
gets(s);
len = strlen(s);
i = 0;
while (i<len) {
while (i < len && s[i] == ' ')
i++;
while (i < len && s[i] != ' ')
putc(s[i++], stdout);
putc('\n', stdout);
}
return 0;
}
However, it's not that clean, a professional coder would tend to opt for more modularisation and worthwhile comments, something along the lines of the following. First, some general functions for skipping spaces and echoing words:
#include <stdio.h>
#include <string.h>
static char *skipSpaces (char *pStr) {
// Just throw away spaces, returning address of first non-space.
while (*pStr == ' ')
pStr++;
return pStr;
}
static char *echoWord (char *pStr) {
// If at end of string, say so.
if (*pStr == '\0')
return NULL;
// Echo the word itself.
while ((*pStr != '\0') && (*pStr != ' '))
putchar (*pStr++);
// Then skip to start of next word (or end of string).
pStr = skipSpaces (pStr);
// Return new pointer.
return pStr;
}
Then, with that done, the main program becomes a lot easier to understand:
int main (void) {
// Get string from user in a more safe manner.
char str[50];
printf("Enter string: ");
fgets (str, sizeof (str), stdin);
// Remove newline if there.
size_t len = strlen (str);
if ((len > 0) && (str[len-1] == '\n'))
str[len-1] = '\0';
// Start with first real character.
char *pStr = skipSpaces (str);
// Process words and space groups (adding newline) until at end of string.
while ((pStr = echoWord (pStr)) != NULL)
putchar ('\n');
return 0;
}

Resources