System command as variable in C - c

I would like to get which command as a parameter of execv. For example user types "firefox" as an input. It will show the file path with which firefox command and i would like to use it in execv.
Here is my try :
int main(void)
{
//char inputBuffer[MAX_LINE]; /*buffer to hold command entered */
char *args[MAX_LINE/2 + 1]; /*command line arguments */
while (1){
char command1[50] = "which ";
char *input = args[0]; /* args[0] holds input */
strcat(command1,input);
char *const parmList[] = {input,NULL};
char buf[100]; /* will convert command to the file path */
char *str;
FILE *file;
if (NULL == (file = popen(command1, "r"))) {
perror("popen");
exit(EXIT_FAILURE);
}
while (fgets(buf, sizeof(buf), file) != NULL) {
}
pclose(file);
int count=0;
for(int m=0;m<buf[m];m++){
if(buf[m] != ' ')
count++;
}
char x5[count];
for(int t=0;t<=count;t++){
x5[t]=buf[t];
}
printf("%s\n\n\n\n",x5); /*i tried to delete spaces from 'buf'
but there is something wrong in file path the output is something like that :
(input : firefox)
usr/bin/firefox
(some weird characters)
because of weird characters execv don't get filepath correctly. I have to this
only with execv
*/
pid_t childpid;
childpid = fork();
if (childpid == -1) {
printf("error");
}
if (childpid == 0) {
execv(x5, parmList);
}
}
}

there are, as you noticed, a few problems with the two loops that are trying to remove any embedded spaces in the value being passed as first param to execv()
Note: always place the literal on the left to have compiler catch error if accidentally wrote an assignment rather than a comparison statement.
1) failed to trim any trailing newline from the buf[] array.
2) those loops are, well, not correct. Suggest:
// remove trailing newline
char * newline = NULL;
if( NULL != (newline = strstr( buf, "\n") ) )
{ // then trim newline
*newline = '\0';
}
// data declarations for removing space characters
char *sourceIndex = buf;
char *targetIndex = buf;
// following loop removes space characters, in place
// and stops when the string terminator char encountered
for( ; sourceIndex; sourceIndex++)
{
if( ' ' != *sourceIndex )
{ // then char to have in final output
*targetIndex = *sourceIndex; // copy it
targetIndex++; // step to next position in target
}
}
*targetIndex = '\0'; // terminate the (possibly shorter) string
...
// pass to new process
if( 0 == childpid )
{ // then child process
execv(buf, parmList); // only returns if execv() failed
perror( "execv failed" );
exit( EXIT_FAILURE );
}

Related

Unable to run ./program with exec and makeargv in c

So to start off, this program has two main parts. The first takes input from the command line using fgets and then makes an argv array with a function called makeargv. The second, takes that argv array and runs it using execvp. The problem I am having is that the program will only run system programs such as "ls", "pwd", "vim", etc. but will not run any program when the directory is specified, such as "./program". I have already tried different versions of exec but the only difference that has made is that then my program will no longer run any commands.
For the below program I cut out all the code that was not relevant to the question to avoid confusion.
#ifndef MAX_CANON
#define MAX_CANON 8192
#endif
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (int argc, char *argv[]) {
char cmd[MAX_CANON];
char delim[] = "\t";
char **myargv;
printf("Beginning program...\nEnter a command to execute:\n");
while(fgets(cmd, MAX_CANON, stdin) != NULL){ // Here's where I get input from the command line
/* Remove newline from end of command */
if (*(cmd + strlen(cmd) - 1) == '\n' || *(cmd + strlen(cmd) - 1) == ' ' )
*(cmd + strlen(cmd) - 1) = 0;
/*---- Child Code ----*/
if((p = fork()) == 0){
if (makeargv(cmd, delim, &myargv) == -1) { // Here is where I make the argv array
fprintf(stderr, "Child failed to construct an argument array for %s\n", &cmd[0]);
return 1;
}
fprintf(stderr, "Command is: %s\n", *&myargv[0]);
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
return 0;
}
/*---- Parent Code ----*/
Here is the makeargv code
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int makeargv(const char *s, const char *delimiters, char ***argvp) {
int error;
int i;
int numtokens;
const char *snew;
char *t;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
errno = EINVAL;
return -1;
}
*argvp = NULL;
snew = s + strspn(s, delimiters); /* snew is real start of string */
if ((t = malloc(strlen(snew) + 1)) == NULL)
return -1;
strcpy(t, snew);
numtokens = 0;
if (strtok(t, delimiters) != NULL) /* count the number of tokens in s */
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
/* create argument array for ptrs to the tokens */
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
error = errno;
free(t);
errno = error;
return -1;
}
/* insert pointers to tokens into the argument array */
if (numtokens == 0)
free(t);
else {
strcpy(t, snew);
**argvp = strtok(t, delimiters);
for (i = 1; i < numtokens; i++)
*((*argvp) + i) = strtok(NULL, delimiters);
}
*((*argvp) + numtokens) = NULL; /* put in final NULL pointer */
return numtokens;
}
Edit:
Swapped fprintf for perror.
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
perror("Error: Failed to execute command!\n");
return 1;
}
I am now getting a "No such file or directory" error.
FIXED:
The makeargv program was using "\t" as it's delimeter instead of " " so it was not creating the array correctly.
Changing:
char delim[] = "\t";
To:
char delim[] = " ";
Fixes the problem.
There is 2 type of exec:
the first need a path as the environment PATH who contain a location (where find sys executables)
and the others how take real file location.
Form the man page (https://linux.die.net/man/3/exec):
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character.
So this mean that you need to use an absolute location, who always begin with /.
You can use getwd() and strcat() to concatenate the 2 strings.
For execvp:
int execvp(const char *file, char *const argv[]); // From the man (I <3 mans)
So:
char argv[3][] = { "/bin/ls", "/", NULL};
if (execvp("/bin/ls", argv) == -1)
return (1);
For evecv:
int execv(const char *path, char *const argv[]);
There is a problem, it need the system PATH. If you don't know what it is type "echo $PATH" in bash. It's a list of directories where the system can find binaries like "ls" in "/bin", concatenate with ":" as sparator. I find the exact definition here.
For your makeargv:
I don't understand why you deference it and take the address after.
execvp(myargv[0], &myargv[0])
char **myargv; //That's we have
char *const argv[]; //that's we need
//so you should try simply:
execvp(myargv[0], myargv);
//or if there is a cast problem
execvp(myargv[0], (char *const[])myargv);
Good luck!

Create a shell in c that recognizes comments

I am trying to write a shell in c that will recognize # as a comment character. For example, if I type the command "ls # This is a comment", the program should not recognize the characters after the #.
Here is the code I currently have:
/* See Chapter 5 of Advanced UNIX Programming: http://www.basepath.com/aup/
* for further related examples of systems programming. (That home page
* has pointers to download this chapter free.
*
* Copyright (c) Gene Cooperman, 2006; May be freely copied as long as this
* copyright notice remains. There is no warranty.
*/
/* To know which "includes" to ask for, do 'man' on each system call used.
* For example, "man fork" (or "man 2 fork" or man -s 2 fork") requires:
* <sys/types.h> and <unistd.h>
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#define MAXLINE 200 /* This is how we declare constants in C */
#define MAXARGS 20
/* In C, "static" means not visible outside of file. This is different
* from the usage of "static" in Java.
* Note that end_ptr is an output parameter.
*/
static char * getword(char * begin, char **end_ptr) {
char * end = begin;
while ( *begin == ' ' )
begin++; /* Get rid of leading spaces. */
end = begin;
while ( *end != '\0' && *end != '\n' && *end != ' ' )
end++; /* Keep going. */
if ( end == begin )
return NULL; /* if no more words, return NULL */
*end = '\0'; /* else put string terminator at end of this word. */
*end_ptr = end;
if (begin[0] == '$') { /* if this is a variable to be expanded */
begin = getenv(begin+1); /* begin+1, to skip past '$' */
if (begin == NULL) {
perror("getenv");
begin = "UNDEFINED";
}
}
return begin; /* This word is now a null-terminated string. return it. */
}
/* In C, "int" is used instead of "bool", and "0" means false, any
* non-zero number (traditionally "1") means true.
*/
/* argc is _count_ of args (*argcp == argc); argv is array of arg _values_*/
static void getargs(char cmd[], int *argcp, char *argv[])
{
char *cmdp = cmd;
char *end;
int i = 0;
/* fgets creates null-terminated string. stdin is pre-defined C constant
* for standard intput. feof(stdin) tests for file:end-of-file.
*/
if (fgets(cmd, MAXLINE, stdin) == NULL && feof(stdin)) {
printf("Couldn't read from standard input. End of file? Exiting ...\n");
exit(1); /* any non-zero value for exit means failure. */
}
while ( (cmdp = getword(cmdp, &end)) != NULL ) { /* end is output param */
/* getword converts word into null-terminated string */
if (strchr(cmdp, '#') != NULL) {
}
argv[i++] = cmdp;
/* "end" brings us only to the '\0' at end of string */
cmdp = end + 1;
}
argv[i] = NULL; /* Create additional null word at end for safety. */
*argcp = i;
}
static void execute(int argc, char *argv[])
{
pid_t childpid; /* child process ID */
childpid = fork();
if (childpid == -1) { /* in parent (returned error) */
perror("fork"); /* perror => print error string of last system call */
printf(" (failed to execute command)\n");
}
if (childpid == 0) { /* child: in child, childpid was set to 0 */
/* Executes command in argv[0]; It searches for that file in
* the directories specified by the environment variable PATH.
*/
if (-1 == execvp(argv[0], argv)) {
perror("execvp");
printf(" (couldn't find command)\n");
}
/* NOT REACHED unless error occurred */
exit(1);
} else /* parent: in parent, childpid was set to pid of child process */
waitpid(childpid, NULL, 0); /* wait until child process finishes */
return;
}
int main(int argc, char *argv[])
{
char cmd[MAXLINE];
char *childargv[MAXARGS];
int childargc;
while (1) {
printf("%% "); /* printf uses %d, %s, %x, etc. See 'man 3 printf' */
fflush(stdout); /* flush from output buffer to terminal itself */
getargs(cmd, &childargc, childargv); /* childargc and childargv are
output args; on input they have garbage, but getargs sets them. */
/* Check first for built-in commands. */
if ( childargc > 0 && strcmp(childargv[0], "exit") == 0 )
exit(0);
else if ( childargc > 0 && strcmp(childargv[0], "logout") == 0 )
exit(0);
else
execute(childargc, childargv);
}
/* NOT REACHED */
}
You have to put the test outside of the while loop.
static void getargs(char cmd[], int *argcp, char *argv[])
{
char *cmdp = cmd;
char *end, *hash;
int i = 0;
/* fgets creates null-terminated string. stdin is pre-defined C constant
* for standard intput. feof(stdin) tests for file:end-of-file.
*/
if (fgets(cmd, MAXLINE, stdin) == NULL && feof(stdin)) {
printf("Couldn't read from standard input. End of file? Exiting ...\n");
exit(1); /* any non-zero value for exit means failure. */
}
// check if we have a comment
hash = strchr(cmd,'#');
if(hash != NULL){
// just overwrite it with NULs
while(*hash != '\0'){
*hash = '\0';
hash++;
}
}
while ( (cmdp = getword(cmdp, &end)) != NULL ) { /* end is output param */
/* getword converts word into null-terminated string */
argv[i++] = cmdp;
/* "end" brings us only to the '\0' at end of string */
cmdp = end + 1;
}
argv[i] = NULL; /* Create additional null word at end for safety. */
*argcp = i;
}

c read block of lines and store them [duplicate]

I am really new to C, and the reading files thing drives me crazy...
I want read a file including name, born place and phone number, etc. All separated by tab
The format might be like this:
Bob Jason Los Angeles 33333333
Alice Wong Washington DC 111-333-222
So I create a struct to record it.
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
I tried many ways to read this file into struct but it failed.
I tired fread:
read_file = fopen("read.txt", "r");
Person temp;
fread(&temp, sizeof(Person), 100, read_file);
printf("%s %s %s \n", temp.name, temp.address, temp.phone);
But char string does not recorded into temp separated by tab, it read the whole file into temp.name and get weird output.
Then I tried fscanf and sscanf, those all not working for separating tab
fscanf(read_file, "%s %s %s", temp.name, temp.address, temp.phone);
Or
fscanf(read_file, "%s\t%s\t%s", temp.name, temp.address, temp.phone);
This separates the string by space, so I get Bob and Jason separately, while indeed, I need to get "Bob Jason" as one char string. And I did separate these format by tab when I created the text file.
Same for sscanf, I tried different ways many times...
Please help...
I suggest:
Use fgets to read the text line by line.
Use strtok to separate the contents of the line by using tab as the delimiter.
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
char* token = strtok(line, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
// Copy token at most the number of characters
// temp.name can hold. Similar logic applies to address
// and phone number.
temp.name[0] = '\0';
strncat(temp.name, token, sizeof(temp.name)-1);
}
token = strtok(NULL, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.address[0] = '\0';
strncat(temp.address, token, sizeof(temp.address)-1);
}
token = strtok(NULL, "\n");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.phone[0] = '\0';
strncat(temp.phone, token, sizeof(temp.phone)-1);
}
Update
Using a helper function, the code can be reduced in size. (Thanks #chux)
// The helper function.
void copyToken(char* destination,
char* source,
size_t maxLen;
char const* delimiter)
{
char* token = strtok(source, delimiter);
if ( token != NULL )
{
destination[0] = '\0';
strncat(destination, token, maxLen-1);
}
}
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
copyToken(temp.name, line, sizeof(temp.name), "\t");
copyToken(temp.address, NULL, sizeof(temp.address), "\t");
copyToken(temp.phone, NULL, sizeof(temp.phone), "\n");
This is only for demonstration, there are better ways to initialize variables, but to illustrate your main question i.e. reading a file delimited by tabs, you can write a function something like this:
Assuming a strict field definition, and your struct definition you can get tokens using strtok().
//for a file with constant field definitions
void GetFileContents(char *file, PERSON *person)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int i = -1;
fp = fopen(file, "r");
while(fgets(line, 260, fp))
{
i++;
buf = strtok(line, "\t\n");
if(buf) strcpy(person[i].name, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].address, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].phone, buf);
//Note: if you have more fields, add more strtok/strcpy sections
//Note: This method will ONLY work for consistent number of fields.
//If variable number of fields, suggest 2 dimensional string array.
}
fclose(fp);
}
Call it in main() like this:
int main(void)
{
//...
PERSON person[NUM_LINES], *pPerson; //NUM_LINES defined elsewhere
//and there are better ways
//this is just for illustration
pPerson = &person[0];//initialize pointer to person
GetFileContents(filename, pPerson); //call function to populate person.
//...
return 0;
}
First thing,
fread(&temp, sizeof(temp), 100, read_file);
will not work because the fields are not fixed width, so it will always read 20 characters for name 30 for address and so on, which is not always the correct thing to do.
You need to read one line at a time, and then parse the line, you can use any method you like to read a like, a simple one is by using fgets() like this
char line[100];
Person persons[100];
int index;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
persons[i++] = parseLineAndExtractPerson(line);
}
Now we need a function to parse the line and store the data in you Person struct instance
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
Here is a sample implementation of a loop to read at most 100 records
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/path/to/the/file.type", "r");
if (read_file == NULL)
return -1;
index = 0;
while ((index < 100) && (fgets(line, sizeof(line), read_file) != NULL))
{
size_t length;
/* remove the '\n' left by `fgets()'. */
length = strlen(line);
if ((length > 0) && (line[length - 1] == '\n'))
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Here is a complete program that does what I think you need
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/home/iharob/data.dat", "r");
if (read_file == NULL)
return -1;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
size_t length;
length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Parsing strings returned by fgets can be very annoying, especially when input is truncated. In fact, fgets leaves a lot to be desired. Did you get the correct string or was there more? Is there a newline at the end? For that matter, is the end 20 bytes away or 32768 bytes away? It would be nice if you didn't need to count that many bytes twice -- once with fgets and once with strlen, just to remove a newline that you didn't want.
Things like fscanf don't necessarily work as intended in this situation unless you have C99's "scanset" feature available, and then that will automatically add a null terminator, if you have enough room. The return value of any of the scanf family is your friend in determining whether success or failure occurred.
You can avoid the null terminator by using %NNc, where NN is the width, but if there's a \t in those NN bytes, then you need to separate it and move it to the next field, except that means bytes in the next field must be moved to the field after that one, and the 90th field will need its bytes moved to the 91st field... And hopefully you only need to do that once... Obviously that isn't actually a solution either.
Given those reasons, I feel it's easier just to read until you encounter one of the expected delimiters and let you decide the behavior of the function when the size specified is too small for a null terminator, yet large enough to fill your buffer. Anyway, here's the code. I think it's pretty straightforward:
/*
* Read a token.
*
* tok: The buffer used to store the token.
* max: The maximum number of characters to store in the buffer.
* delims: A string containing the individual delimiter bytes.
* fileptr: The file pointer to read the token from.
*
* Return value:
* - max: The buffer is full. In this case, the string _IS NOT_ null terminated.
* This may or may not be a problem: it's your choice.
* - (size_t)-1: An I/O error occurred before the last delimiter
* (just like with `fgets`, use `feof`).
* - any other value: The length of the token as `strlen` would return.
* In this case, the string _IS_ null terminated.
*/
size_t
read_token(char *restrict tok, size_t max, const char *restrict delims,
FILE *restrict fileptr)
{
int c;
size_t n;
for (n = 0; n < max && (c = getchar()) != EOF &&
strchr(delims, c) == NULL; ++n)
*tok++ = c;
if (c == EOF)
return (size_t)-1;
if (n == max)
return max;
*tok = 0;
return n;
}
Usage is pretty straightforward as well:
#include <stdio.h>
#include <stdlib.h>
typedef struct person {
char name[20];
char address[30];
char phone[20];
} Person;
int
main(void)
{
FILE *read_file;
Person temp;
size_t line_num;
size_t len;
int c;
int exit_status = EXIT_SUCCESS;
read_file = fopen("read.txt", "r");
if (read_file == NULL) {
fprintf(stderr, "Error opening read.txt\n");
return 1;
}
for (line_num = 0;; ++line_num) {
/*
* Used for detecting early EOF
* (e.g. the last line contains only a name).
*/
temp.name[0] = temp.phone[0] = 0;
len = read_token(temp.name, sizeof(temp.name), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.address, sizeof(temp.address), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.phone, sizeof(temp.phone), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
// Do something with the input here. Example:
printf("Entry %zu:\n"
"\tName: %.*s\n"
"\tAddress: %.*s\n"
"\tPhone: %.*s\n\n",
line_num + 1,
(int)sizeof(temp.name), temp.name,
(int)sizeof(temp.address), temp.address,
(int)sizeof(temp.phone), temp.phone);
}
if (ferror(read_file)) {
fprintf(stderr, "error reading from file\n");
exit_status = EXIT_FAILURE;
}
else if (feof(read_file) && temp.phone[0] == 0 && temp.name[0] != 0) {
fprintf(stderr, "Unexpected end of file while reading entry %zu\n",
line_num + 1);
exit_status = EXIT_FAILURE;
}
//else feof(read_file) is still true, but we parsed a full entry/record
fclose(read_file);
return exit_status;
}
Notice how the exact same 8 lines of code appear in the read loop to handle the return value of read_token? Because of that, I think there's probably room for another function to call read_token and handle its return value, allowing main to simply call this "read_token handler", but I think the code above gives you the basic idea about how to work with read_token and how it can apply in your situation. You might change the behavior in some way, if you like, but the read_token function above would suit me rather well when working with delimited input like this (things would be a bit more complex when you add quoted fields into the mix, but not much more complex as far as I can tell). You can decide what happens with max being returned. I opted for it being considered an error, but you might think otherwise. You might even add an extra getchar when n == max and consider max being a successful return value and something like (size_t)-2 being the "token too large" error indicator instead.

Read files separated by tab in c

I am really new to C, and the reading files thing drives me crazy...
I want read a file including name, born place and phone number, etc. All separated by tab
The format might be like this:
Bob Jason Los Angeles 33333333
Alice Wong Washington DC 111-333-222
So I create a struct to record it.
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
I tried many ways to read this file into struct but it failed.
I tired fread:
read_file = fopen("read.txt", "r");
Person temp;
fread(&temp, sizeof(Person), 100, read_file);
printf("%s %s %s \n", temp.name, temp.address, temp.phone);
But char string does not recorded into temp separated by tab, it read the whole file into temp.name and get weird output.
Then I tried fscanf and sscanf, those all not working for separating tab
fscanf(read_file, "%s %s %s", temp.name, temp.address, temp.phone);
Or
fscanf(read_file, "%s\t%s\t%s", temp.name, temp.address, temp.phone);
This separates the string by space, so I get Bob and Jason separately, while indeed, I need to get "Bob Jason" as one char string. And I did separate these format by tab when I created the text file.
Same for sscanf, I tried different ways many times...
Please help...
I suggest:
Use fgets to read the text line by line.
Use strtok to separate the contents of the line by using tab as the delimiter.
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
char* token = strtok(line, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
// Copy token at most the number of characters
// temp.name can hold. Similar logic applies to address
// and phone number.
temp.name[0] = '\0';
strncat(temp.name, token, sizeof(temp.name)-1);
}
token = strtok(NULL, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.address[0] = '\0';
strncat(temp.address, token, sizeof(temp.address)-1);
}
token = strtok(NULL, "\n");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.phone[0] = '\0';
strncat(temp.phone, token, sizeof(temp.phone)-1);
}
Update
Using a helper function, the code can be reduced in size. (Thanks #chux)
// The helper function.
void copyToken(char* destination,
char* source,
size_t maxLen;
char const* delimiter)
{
char* token = strtok(source, delimiter);
if ( token != NULL )
{
destination[0] = '\0';
strncat(destination, token, maxLen-1);
}
}
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
copyToken(temp.name, line, sizeof(temp.name), "\t");
copyToken(temp.address, NULL, sizeof(temp.address), "\t");
copyToken(temp.phone, NULL, sizeof(temp.phone), "\n");
This is only for demonstration, there are better ways to initialize variables, but to illustrate your main question i.e. reading a file delimited by tabs, you can write a function something like this:
Assuming a strict field definition, and your struct definition you can get tokens using strtok().
//for a file with constant field definitions
void GetFileContents(char *file, PERSON *person)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int i = -1;
fp = fopen(file, "r");
while(fgets(line, 260, fp))
{
i++;
buf = strtok(line, "\t\n");
if(buf) strcpy(person[i].name, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].address, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].phone, buf);
//Note: if you have more fields, add more strtok/strcpy sections
//Note: This method will ONLY work for consistent number of fields.
//If variable number of fields, suggest 2 dimensional string array.
}
fclose(fp);
}
Call it in main() like this:
int main(void)
{
//...
PERSON person[NUM_LINES], *pPerson; //NUM_LINES defined elsewhere
//and there are better ways
//this is just for illustration
pPerson = &person[0];//initialize pointer to person
GetFileContents(filename, pPerson); //call function to populate person.
//...
return 0;
}
First thing,
fread(&temp, sizeof(temp), 100, read_file);
will not work because the fields are not fixed width, so it will always read 20 characters for name 30 for address and so on, which is not always the correct thing to do.
You need to read one line at a time, and then parse the line, you can use any method you like to read a like, a simple one is by using fgets() like this
char line[100];
Person persons[100];
int index;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
persons[i++] = parseLineAndExtractPerson(line);
}
Now we need a function to parse the line and store the data in you Person struct instance
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
Here is a sample implementation of a loop to read at most 100 records
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/path/to/the/file.type", "r");
if (read_file == NULL)
return -1;
index = 0;
while ((index < 100) && (fgets(line, sizeof(line), read_file) != NULL))
{
size_t length;
/* remove the '\n' left by `fgets()'. */
length = strlen(line);
if ((length > 0) && (line[length - 1] == '\n'))
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Here is a complete program that does what I think you need
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/home/iharob/data.dat", "r");
if (read_file == NULL)
return -1;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
size_t length;
length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Parsing strings returned by fgets can be very annoying, especially when input is truncated. In fact, fgets leaves a lot to be desired. Did you get the correct string or was there more? Is there a newline at the end? For that matter, is the end 20 bytes away or 32768 bytes away? It would be nice if you didn't need to count that many bytes twice -- once with fgets and once with strlen, just to remove a newline that you didn't want.
Things like fscanf don't necessarily work as intended in this situation unless you have C99's "scanset" feature available, and then that will automatically add a null terminator, if you have enough room. The return value of any of the scanf family is your friend in determining whether success or failure occurred.
You can avoid the null terminator by using %NNc, where NN is the width, but if there's a \t in those NN bytes, then you need to separate it and move it to the next field, except that means bytes in the next field must be moved to the field after that one, and the 90th field will need its bytes moved to the 91st field... And hopefully you only need to do that once... Obviously that isn't actually a solution either.
Given those reasons, I feel it's easier just to read until you encounter one of the expected delimiters and let you decide the behavior of the function when the size specified is too small for a null terminator, yet large enough to fill your buffer. Anyway, here's the code. I think it's pretty straightforward:
/*
* Read a token.
*
* tok: The buffer used to store the token.
* max: The maximum number of characters to store in the buffer.
* delims: A string containing the individual delimiter bytes.
* fileptr: The file pointer to read the token from.
*
* Return value:
* - max: The buffer is full. In this case, the string _IS NOT_ null terminated.
* This may or may not be a problem: it's your choice.
* - (size_t)-1: An I/O error occurred before the last delimiter
* (just like with `fgets`, use `feof`).
* - any other value: The length of the token as `strlen` would return.
* In this case, the string _IS_ null terminated.
*/
size_t
read_token(char *restrict tok, size_t max, const char *restrict delims,
FILE *restrict fileptr)
{
int c;
size_t n;
for (n = 0; n < max && (c = getchar()) != EOF &&
strchr(delims, c) == NULL; ++n)
*tok++ = c;
if (c == EOF)
return (size_t)-1;
if (n == max)
return max;
*tok = 0;
return n;
}
Usage is pretty straightforward as well:
#include <stdio.h>
#include <stdlib.h>
typedef struct person {
char name[20];
char address[30];
char phone[20];
} Person;
int
main(void)
{
FILE *read_file;
Person temp;
size_t line_num;
size_t len;
int c;
int exit_status = EXIT_SUCCESS;
read_file = fopen("read.txt", "r");
if (read_file == NULL) {
fprintf(stderr, "Error opening read.txt\n");
return 1;
}
for (line_num = 0;; ++line_num) {
/*
* Used for detecting early EOF
* (e.g. the last line contains only a name).
*/
temp.name[0] = temp.phone[0] = 0;
len = read_token(temp.name, sizeof(temp.name), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.address, sizeof(temp.address), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.phone, sizeof(temp.phone), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
// Do something with the input here. Example:
printf("Entry %zu:\n"
"\tName: %.*s\n"
"\tAddress: %.*s\n"
"\tPhone: %.*s\n\n",
line_num + 1,
(int)sizeof(temp.name), temp.name,
(int)sizeof(temp.address), temp.address,
(int)sizeof(temp.phone), temp.phone);
}
if (ferror(read_file)) {
fprintf(stderr, "error reading from file\n");
exit_status = EXIT_FAILURE;
}
else if (feof(read_file) && temp.phone[0] == 0 && temp.name[0] != 0) {
fprintf(stderr, "Unexpected end of file while reading entry %zu\n",
line_num + 1);
exit_status = EXIT_FAILURE;
}
//else feof(read_file) is still true, but we parsed a full entry/record
fclose(read_file);
return exit_status;
}
Notice how the exact same 8 lines of code appear in the read loop to handle the return value of read_token? Because of that, I think there's probably room for another function to call read_token and handle its return value, allowing main to simply call this "read_token handler", but I think the code above gives you the basic idea about how to work with read_token and how it can apply in your situation. You might change the behavior in some way, if you like, but the read_token function above would suit me rather well when working with delimited input like this (things would be a bit more complex when you add quoted fields into the mix, but not much more complex as far as I can tell). You can decide what happens with max being returned. I opted for it being considered an error, but you might think otherwise. You might even add an extra getchar when n == max and consider max being a successful return value and something like (size_t)-2 being the "token too large" error indicator instead.

Using fgets + dynamic memory allocation

I have a homework problem that I need help with. I need to implement a function char *getStrFromFile(FILE*);. I just simply don't understand it. I have attempted to figure out the question.
This function safely reads a complete line of unknown length from the
open file pointed to by fpin. It returns a line that is at most CHUNKSZ-1
characters longer than the minimum needed to hold the line.
It initially allocates an array of DEFLEN characters to hold the string,
and if this space is inadequate to hold the string, it will iteratively
create a new string that is CHUNKSZ larger, copy the old string to it
release the old string, and then read in more characters from the file,
and continue this until the entire line of arbitrary length can be returned.
RETURNS: NULL if no characters are left in fpin, otherwise:
pointer to allocated array at most CHUNKSZ-1 characters longer than
miminum necessary to hold an arbitrarily long line from file fpin
int main(int nargs, char *args[])
{
FILE *fpin;
char *getStrFromFile(FILE*);
if (nargs != 2)
{
fprintf(stderr, "USAGE: %s <file>\n", args[0]);
exit(1);
}
fpin = fopen(args[1], "r");
while(1)
{
char *ln;
ln = getStrFromFile(fpin);
if (!ln)
break;
printf("%s", ln);
free(ln);
}
fclose(fpin);
return(0);
}
That is the main method I have to use. Here is what I know so far.
char *getStrFromFile(FILE *fpin)
{
char string[DEFLEN];
if(fgets(string, CHUNKSZ, fpin) != NULL) {
int l = lstr(string);
if(string[l-1] = '\n') {
return string;
} else {
int size = 1;
int end = 0;
while (string[l-1] != '\n') {
size += CHUNSZ;
char *s2 = (char*)malloc(sizeof(char)+size);
for(i = 0+end; i < lstr(string); i++) {
s2[i] = string[i];
}
end += lstr(string);
fgets(string, size + end, fpin);
return s2;
This is not correct.
if(string[l-1] = '\n')
it must be
if(string[l-1] == '\n')

Resources