Reputation: 13758
I am new to C, and trying to implement whoami, as an exercise to myself. I have following code:
#define _POSIX_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h> // strtok
int str_to_int(const char *str)
{
int acc = 0;
int i;
for (i = 0; str[i] != '\0'; ++i) {
acc = (10 * acc) + (str[i] - 48); // 48 -> 0 in ascii
}
return acc;
}
int main()
{
FILE *passwd;
char *line = NULL;
size_t line_size;
passwd = fopen("/etc/passwd","r");
uid_t uid = getuid();
while (getline(&line, &line_size,passwd) != -1) {
char *name = strtok(line,":");
strtok(line,":"); // passwd
char *user_id = strtok(line,":");
if (str_to_int(user_id) == uid) {
printf("%s\n",name);
break;
}
}
fclose(passwd);
return 0;
}
Do I need to save line pointer inside of the while loop. Because I think strtok modifies it somehow, but I am not sure if I need to copy the line, or starting address of the line before I use it with strtok.
Upvotes: 1
Views: 4420
Reputation: 484
I totally agree with geekosaur (and Mark). Paraphrasing his comment, you can modify the above code as following:
while (getline(&line, &line_size, passwd) != -1) {
char *name = strtok(line,":");
strtok(NULL,":"); // passwd
char *user_id = strtok(NULL,":");
if (str_to_int(user_id) == uid) {
printf("%s\n",name);
break;
}
}
You should pass NULL for the strtok
invocations other than the first one.
Upvotes: 0
Reputation: 41242
It might be safer to use strtok_r
. It is safer in a multi-threaded situation. That may not apply in this case, but it is sometimes better just to assume that some point any snippet you write might end up in a multi-threaded app. The following is the OP code modified to use strtok_r
.
char *pos;
char *name = strtok_r(line,":",&pos);
strtok_r(NULL,":",&pos); // passwd
char *user_id = strtok_r(NULL,":",&pos);
And, yes, strtok
(and strtok_r
) do modify the given input buffer (first parameter). But it can be safe if used properly. Since strtok
returns a pointer to a buffer inside the given string, you need to be careful how you use it. In your case, when it breaks out of the loop, name
and user_id
will point to a value inside the line
buffer.
And you maybe should read the man pages for getline
. The way you are using it, it returns an allocated buffer that your application is responsible for freeing. That might be what you are aiming for, but I mention it because I don't see a free
call for it in the posted code.
Upvotes: 0
Reputation: 61439
strtok
is a horrid function. I don't know what documentation you read (if any?) but it both modifies the buffer it is passed and retains an internal pointer into the buffer; you should only pass the buffer the first time you use it on a given line, and pass NULL
subsequently so it knows to pick up where it left off instead of starting at the beginning again (which won't actually work quite right because it stomped on the buffer...).
Better, find some other way to parse and stay far away from strtok
.
Upvotes: 1