polslinux
polslinux

Reputation: 1779

C find string into file

This is the file auth.txt (username password)
paolo 1234
luca 0000
marci 1000

And this is my code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void onexit(char *u, char *p, char *l, FILE *f, int flag);

int main(int argc, char *argv[]){

    FILE *fp;
    char *tmp, *tmp2, *user, *pass, *line;

    printf("Inserire utente: ");
    if(scanf("%ms", &user) == EOF){
        perror("scanf");
        return EXIT_FAILURE;
    }

    printf("Inserire password: ");
    if(scanf("%ms", &pass) == EOF){
        perror("scanf");
        onexit(user, NULL, NULL, NULL, 1);
        return EXIT_FAILURE;
    }

    size_t max_length = strlen(user) + strlen(pass) + 3;
    line = malloc(max_length);
    if(line == NULL){
        perror("malloc");
        onexit(user, pass, NULL, NULL, 2);
        return EXIT_FAILURE;
    }

    fp = fopen("/home/pol/auth.txt", "r");
    if(fp == NULL){
        printf("Errore apertura file\n");
        onexit(user, pass, line, NULL, 3);
        return EXIT_FAILURE;
    }

    while(!feof(fp)){
        if(fgets(line, max_length , fp) == NULL){
            perror("fgets");
            onexit(user, pass, line, fp, 4);
            return EXIT_FAILURE;
        }
        tmp = strtok(line, " ");
        if(tmp == NULL){
            perror("strtok");
            onexit(user, pass, line, fp, 4);
            return EXIT_FAILURE;
        }

        tmp2 = strtok(NULL, "\n");
        if(tmp2 == NULL){
            perror("strtok");
            onexit(user, pass, line, fp, 4);
            return EXIT_FAILURE;
        }
        if((strcmp(tmp,user) == 0) && (strcmp(tmp2,pass) == 0)){
            printf("USER: %s - PASS: %s\n", tmp, tmp2);
            onexit(user, pass, line, fp, 4);
            return EXIT_SUCCESS;
        }
        else{
                 continue;
        }
    }
    printf("no such user or pwd into DB\n");
    onexit(user, pass, line, fp, 4);
    return EXIT_FAILURE;
}

void onexit(char *u, char *p, char *l, FILE *f, int flag){
    if(flag == 1){
        free(u);
    }
    if(flag == 2){
        free(u);
        free(p);
    }
    if(flag == 3){
        free(u);
        free(p);
        free(l);
    }
    if(flag == 4){
        free(u);
        free(p);
        free(l);
        fclose(f);
    }
}

EDIT: another problem!
if i want to test luca with 0000 i got:

Inserire utente: luca
Inserire password: 0000
strtok: Success

and the program stop. It works only with 1st and 3rd input but not with the 2nd!
why??

Upvotes: 1

Views: 1106

Answers (2)

Nate Kohl
Nate Kohl

Reputation: 35924

It looks like your while loop will only go through one iteration:

while(!feof(fp)){
  // ...
  if((strcmp(tmp,user) == 0) && (strcmp(tmp2,pass) == 0)){  // if match, exit
    printf("USER: %s - PASS: %s\n", tmp, tmp2);
    onexit(user, pass, line, fp, 4);
    return EXIT_SUCCESS;
  }
  else{                                                     // else exit
    printf("no such user or pwd into DB\n");
    onexit(user, pass, line, fp, 4);
    return EXIT_FAILURE;
  }
}

...which seems a little funny. Do you really want to exit if the first line you read doesn't match?


Edit:

Another problem: you need to allocate space for user and pass before you send them into scanf:

char *user;
scanf("%ms", &user);  // error

Instead of that, you could try something like:

char user[100];
scanf("%ms", &user);  // now user actually has some space to store data

(In your version, scanf will try to write data into the location that user points to, but user just points at a random location in memory, so who knows what will happen.)

Whoops, as @MvG pointed out, this is fine. I wasn't familiar with the "%ms" POSIX extension.


Edit 2:

Say you enter the username uu and the password pp. Because of this:

size_t max_length = strlen(user) + strlen(pass) + 3;

...you'll set max_length to 2 + 2 + 3, or 7. fgets will read data until it encounters at most max_length-1 characters or a newline character. That means when you get inside your while loop, you'll only try to read at most 7-1 or 6 characters per iteration, which doesn't seem right.

For example, if your password file contains:

paolo 1234
luca 0000
marci 1000

...then line will get the following values:

  1. "paolo " (with a trailing space)
  2. "1234"
  3. "luca 0"
  4. "000"
  5. "marci " (again, with a trailing space)
  6. "1000"

Instead of basing max_length on the current username and password that you've read in, make max_length a large fixed value (like 100).

Upvotes: 3

MvG
MvG

Reputation: 60908

Your error handler is inside the while loop. If the user isn't the first in the file, you abort immediately. Beter write it this way:

…
        if((strcmp(tmp,user) == 0) && (strcmp(tmp2,pass) == 0)){
            printf("USER: %s - PASS: %s\n", tmp, tmp2);
            onexit(user, pass, line, fp, 4);
            return EXIT_SUCCESS;
        }
    }
    printf("no such user or pwd into DB\n");
    onexit(user, pass, line, fp, 4);
    return EXIT_FAILURE;
}

Edit:

I can reproduce the strtok: Success problem when there is an entry in the database without a space in between. This might even be an empty line at the end of the file, in cases where the loop proceeds this far. In that case, the first strtok will return the whole string, and the second strtok will return NULL, thus indicating that there are no more tokens. As this is no error message, the errno status is zero, corresponding to the “error message” Success.

I guess in your case the problem lies in the different length of the records. You measure the length of your line read buffer by the length of the input, but some records are longer. In that case, you read part of a line and split that correctly, and at the next run you read the remainder of the line and end up with what appears like a line without a space, including the behaviour I described above.

Upvotes: 1

Related Questions