I am trying to make a shell program and am working on mkdir, I am reading in the user input and breaking it into segments using sscanf and then trying to use strcmp to check if the first word was "mkdir", the strcmp works if I leave the stdin as is without using sscanf. Can someone tell me why it doesn't work like this, and how to fix it?
Thanks,
//#include "parser.c"
#include <stdio.h>
# include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef MAX_BUF
#define MAX_BUF 200
#endif
int main(void)
{
while (1) {
printf("Enter a command: ");
char input[20];
char line[4][20];
fgets(input, 20, stdin);
sscanf(input, "%s %s", line[0], line[1]);
if(strcmp(line[0], "mkdir")) {
char path[MAX_BUF];
getcwd(path, MAX_BUF);
strcat(path, "/");
strcat(path, line[1]);
mkdir(path, 0700);
printf(path);
}
//printf("output %s", input);
}
}
Change your expression:
strcmp(line[0], "mkdir")
into:
strcmp(line[0], "mkdir") == 0
A match returns zero(a) which, when treated as a boolean, is false rather than true. In other words, your if is currrenly firing for anything other than mkdir.
There are a few other problems with your code, such as its inability to create directories longer than about thirteen characters (buffer size twenty minus one for the '\0' minus six for the "mkdir "), and the fact line[1] will be some arbitrary value if you were to just type in mkdir on its own. That won't end well :-)
You could get around that last one by checking to ensure the correct number of things were scanned, with something like:
if (sscanf(input, "%s %s", line[0], line[1]) != 2) {
output_some_error();
} else {
your_current_processing_of_line1_and_line2();
}
The general rule is: if a failure can affect your ability to proceed safely, you should probably check for, and act on, that failure.
(a) The call strcmp(a, b) will return a value as per the following table:
Situation:
a < b
a == b
a > b
Returns:
< 0
0
> 0
Boolean equivalent:
true
false
true
Related
i have a question on a C program that I'm doing. The beginning of the track ask this:
"Process P ask as argument the path of a file in which every line ust be 16 characters length (included the end of line), and every line must start with "WAIT" or "NOWAIT" followed by a command."
The example of input file is:
WAIT ls
NOWAIT who
WAIT date
I made this code for now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define MIN_SIZE 5
#define ROW_LEN 17
int main (int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Program usage: %s file_path.\n", argv[0]);
exit(1);
}
int fd = open(argv[1], O_RDONLY);
struct stat fd_info;
if(fd < 0) {
perror("Error opening file");
exit(2);
}
fstat(fd, &fd_info);
if(fd_info.st_size <= MIN_SIZE) {
printf("Size of file '%s' is less or equal than 5 bytes.\n", argv[1]);
exit(3);
}
char buf[ROW_LEN];
buf[ROW_LEN - 1] = '\0';
while ((read(fd, buf, ROW_LEN - 1)) > 0) {
char type[ROW_LEN], cmd[ROW_LEN];
sscanf(buf, "%s %s", type, cmd);
printf("type=%s; command=%s;\n", type, cmd);
}
return 0;
}
In this way i can read good only if in the file.txt I complete every row with spaces until it reaches 15 characters for each line (else it start reading next line too). But in the file that prof gave us there aren't spaces to complete the row. So my question is, how can I read correctly from the file? I can't understand that "every line must have 16 characters included end of line".
Thanks to all, I hope I explained good the question!
Firstly with this sentence you must considerate each line as a possible input, but the input is coming from anyone so anything can append and any errors in consequences.
You start on the good way
you must consider all your file and after your line > check if your line is good
you can use getline to get your file easily, and strlen and strcmp to check if your line is conform.
Finaly the part "include end of line", that mean that all the line must have a length of 16 character with '\0', so in your file the "visible" length must be at 15 for the maximum,
for example if the maximal length is 3 included end of line :
"abc" : incorrect because it's equal to {'a', 'b', 'c' '\0'};
"ab" : correct because it's equal to {'a', 'b', '\0'};
#include <stdio.h>
#include <stdlib.h>
#define BUFFERSIZE 10
int main(int argc, char *argv[])
{
char address[BUFFERSIZE];
//checking text file on stdin which does not work
if (fgets(address, BUFFERSIZE, stdin) < 42)
{
fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
}
//while reads the redirected file line by line and print the content line by line
while(fgets(address, BUFFERSIZE, stdin) != NULL)
{
printf("%s", address);
}
return 0;
}
Hi, this is my code. Does not work. The problem is that I have a redirected external file adresy.txt into stdin and I need to check if the file has the required number of rows.
The minimum number of rows that a file must have is 42. If it has 42 or more rows the program can continue, if not, it throws out the fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
I tried it this way if (fgets(address, BUFFERSIZE, stdin) < 42) but it still tells me that I can not compare pointer and integer
like so: warning: comparison between pointer and integer
In the code extension I will compare the arguments from the user to what is in adresy.txt therefore I need argc and *argv [] but now i need to solve this.
Any advice how to fix it? Thanks for any help.
There are several problems in your code:
#define BUFFERSIZE 10 is odd as your lines but be at least 42 long.
you compare the pointer returned by fgets to 42 which is nonsense, BTW your compiler warned you.
With your method you actually display only one line out of two
You probably want this:
#define BUFFERSIZE 200 // maximum length of one line
int main(int argc, char *argv[])
{
char address[BUFFERSIZE];
while(fgets(address, BUFFERSIZE, stdin) != NULL)
{
// here the line has been read
if (strlen(address) < 42)
{
// if the length of the string read is < 42, inform user and stop
fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
exit(1);
}
// otherwise print line
printf("%s", address);
}
return 0;
}
I'm trying to create a function that reads an entire file using a specific read size that can change anytime, but the read system call doesn't store the characters properly in the buffer, so far I'm only trying to print until the end of file like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
# define READ_SIZE (42)
int main(int argc, char **argv)
{
int fd;
int rd;
char *buffer;
buffer = malloc(READ_SIZE);
fd = open(argv[1], O_RDONLY);
while ((rd = read(fd, buffer, READ_SIZE)) > 0)
{
printf("%s", buffer);
}
return (0);
}
This is the file that I'm trying to read:
test1234
test123
test1
test2
test3
test4
test
This is the output of my program:
test123
test12
test1
test2
test3
test4
testest123
test12
test1
test2
test3
test4
tes
I can only use malloc, and read to handle this, open is only for testing, and I don't understand why it does this, usually read returns the number of bytes read in that file, and 0 if it reaches the end of file, so it's a bit weird to see this.
The printing of the character array lacks a null character. This is UB with "%s".
printf("%s", buffer); // bad
To limit printing a character array lacking a null character, use a precision modifier. This will print the character array up to that many characters or a null character - which ever is first.
// printf("%s", buffer);
printf("%.*s", rd, buffer);
Debug tip: Print text with sentinels to clearly indicate the result of each print.
printf("<%.*s>\n", rd, buffer);
Besides the very elegant solution provided by chux's answer you could as well just terminate the buffer (and with this only make it a C-"string") explicitly before printing:
while ((rd = read(fd, buffer, READ_SIZE-1)) > 0) /* read one less, to have a spare
char available for the `0`-terminator. */
{
buffer[rd] = '\0';
printf("'%s'", buffer);
}
I've got a comma-separated list of strings in my file:
Name 1, Name 2, Name 3,
I want to read those names skipping all commas. I've written the following loop:
while(true)
{
if(fscanf(file, "%[^,],", my_string) != 1)
{
break;
}
//...
}
However, it is always executing one more time than it supposed to. Given 3 names in the file, the loop will execute its statements 4 times. Why is this happening? Does EOF indicator rank to my negated scanset [^,]? If so, then how can I solve this issue?
I'm pretty sure this is doing exactly what you want it to. The only modification to the algorithm I made is added the leading whitespace-clear before the % in the format string. Also,I modified this to open the file from a command-line arg. Easier to test that way. Hope thats ok.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc != 2)
return EXIT_FAILURE;
FILE *fp = fopen(argv[1], "r");
if (NULL == fp)
return EXIT_FAILURE;
char str[100];
int count=0;
while (1)
{
if(fscanf(fp, " %[^,],", str) != 1)
break;
printf("%s\n", str);
++count;
}
printf("Parsed %d strings.", count);
return EXIT_SUCCESS;
}
Output
Name 1
Name 2
Name 3
Parsed 3 strings.
I believe the "fourth" execution in the loop you're seeing is the failure condition, which breaks the loop due to failure to parse, unless I'm missing something or not understanding what you're witnessing.
Why are your loop statements executing 4 times. The loop breaks after 4th fscanf statement and codes below aren't executed.EOF indicator doesn't rank to your negated set However maybe you are looking for a more compact solution like this:
while(fscanf(file, "%[^,],", my_string)!=EOF )
{
//do something
}
If not, please post more detailed code and your test file
I've been trying to get this code to work for hours! All I need to do is open a file to see if it is real and readable. I'm new to C so I'm sure there is something stupid I'm missing. Here is the code (shorthand, but copied):
#include <stdio.h>
main() {
char fpath[200];
char file = "/test/file.this";
sprintf(fpath,"~cs4352/projects/proj0%s",file);
FILE *fp = fopen(fpath,"r");
if(fp==NULL) {
printf("There is no file on the server");
exit(1);
}
fclose(fp);
//do more stuff
}
I have also verified that the path is correctly specifying a real file that I have read permissions to. Any other ideas?
Edit 1: I do know that the fpath ends up as "~cs4352/projects/proj0/test/file.this"
Edit 2: I have also tried the using the absolute file path. In both cases, I can verify that the paths are properly built via ls.
Edit 3: There errno is 2... I'm currently trying to track what that means in google.
Edit 4: Ok, errno of 2 is "There is no such file or directory". I am getting this when the reference path in fopen is "/home/courses1/cs4352/projects/proj0/index.html" which I verified does exist and I have read rights to it. As for the C code listed below, there may be a few semantic/newbie errors in it, but gcc does not give me any compile time warnings, and the code works exactly as it should except that it says that it keeps spitting errno of 2. In other words, I know that all the strings/char array are working properly, but the only thing that could be an issue is the fopen() call.
Solution: Ok, the access() procedure is what helped me the most (and what i am still using as it is less code, not to mention the more elegant way of doing it). The problem actually came from something that I didn't explain to you all (because I didn't see it until I used access()). To derrive the file, I was splitting strings using strtok() and was only splitting on " \n", but because this is a UNIX system, I needed to add "\r" to it as well. Once I fixed that, everything fell into place, and I'm sure that the fopen() function would work as well, but I have not tested it.
Thank you all for your helpful suggestions, and especially to Paul Beckingham for finding this wonderful solution.
Cheers!
The "~" is expanded by the shell, and is not expanded by fopen.
To test the existence and readability of a file, consider using the POSIX.1 "access" function:
#include <unistd.h>
if (access ("/path/to/file", F_OK | R_OK) == 0)
{
// file exists and is readable
}
First, file needs to be declared as char* or const char*, not simply char as you've written. But this might just be a typo, the compiler should at least give a warning there.
Secondly, use an absolute path (or a path relative to the current directory), not shell syntax with ~. The substitution of ~cs4352 by the respective home directory is usually done by the shell, but you are directly opening the file. So you are trying to open a file in a ~cs4352 subdirectory of your current working directory, which I guess is not what you want.
Other people have probably produced the equivalent (every modern shell, for example), but here's some code that will expand a filename with ~ or ~user notation.
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <assert.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *relfname(const char *name, char *buffer, size_t bufsiz)
{
assert(name != 0 && buffer != 0 && bufsiz != 0);
if (name[0] != '~')
strncpy(buffer, name, bufsiz);
else
{
const char *copy;
struct passwd *usr = 0;
if (name[1] == '/' || name[1] == '\0')
{
usr = getpwuid(getuid());
copy = &name[1];
}
else
{
char username[PATH_MAX];
copy = strchr(name, '/');
if (copy == 0)
copy = name + strlen(name);
strncpy(username, &name[1], copy - &name[1]);
username[copy - &name[1]] = '\0';
usr = getpwnam(username);
}
if (usr == 0)
return(0);
snprintf(buffer, bufsiz, "%s%s", usr->pw_dir, copy);
}
buffer[bufsiz-1] = '\0';
return buffer;
}
#ifdef TEST
static struct { const char *name; int result; } files[] =
{
{ "/etc/passwd", 1 },
{ "~/.profile", 1 },
{ "~root/.profile", 1 },
{ "~nonexistent/.profile", 0 },
};
#define DIM(x) (sizeof(x)/sizeof(*(x)))
int main(void)
{
int i;
int fail = 0;
for (i = 0; i < DIM(files); i++)
{
char buffer[PATH_MAX];
char *name = relfname(files[i].name, buffer, sizeof(buffer));
if (name == 0 && files[i].result != 0)
{
fail++;
printf("!! FAIL !! %s\n", files[i].name);
}
else if (name != 0 && files[i].result == 0)
{
fail++;
printf("!! FAIL !! %s --> %s (unexpectedly)\n", files[i].name, name);
}
else if (name == 0)
printf("** PASS ** %s (no match)\n", files[i].name);
else
printf("** PASS ** %s -> %s\n", files[i].name, name);
}
return((fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
You could try examining errno for more information on why you're not getting a valid FILE*.
BTW-- in unix the global value errno is set by some library and system calls when they need to return more information than just "it didn't work". It is only guaranteed to be good immediately after the relevant call.
char file = "/test/file.this";
You probably want
char *file = "/test/file.this";
Are you sure you do not mean
~/cs4352/projects/proj0%s"
for your home directory?
To sum up:
Use char *file=/test/file.this";
Don't expect fopen() to do shell substitution on ~ because it won't. Use the full path or use a relative path and make sure the current directory is approrpriate.
error 2 means the file wasn't found. It wasn't found because of item #2 on this list.
For extra credit, using sprintf() like this to write into a buffer that's allocated on the stack is a dangerous habit. Look up and use snprintf(), at the very least.
As someone else here mentioned, using access() would be a better way to do what you're attempting here.