/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Written by Raphaƫl Gertz
*/
/* exit EXIT_FAILURE EXIT_SUCCESS */
#include
/* printf */
#include
/* stat S_ISREG */
#include
/* access R_OK */
#include
/* open O_RDONLY */
#include
/* strspn */
#include
/* Zero ascii offset */
#define ZERO_OFFSET 48
/* Branch cell */
struct branch {
unsigned key;
struct branch *next;
void *child;
};
/* Leaf cell */
struct leaf {
unsigned key;
struct leaf *next;
unsigned count;
};
/* Lookup leaf function prototype */
struct leaf *lookupLeaf(void **, const char *, unsigned);
/* Freeing node function prototype */
void freeTree(void *, unsigned);
/* Main function */
int main(int argc, char **argv) {
/* Stat struct */
struct stat ss;
/* File descriptor */
int fd;
/* Return value */
int ret;
/* Line buffer */
char line[11] = { 0 };
/* Current leaf */
struct leaf *current;
/* Tree root */
struct branch *tree = NULL;
/* Unique counter */
unsigned unique = 0;
/* Duplicated counter */
unsigned duplicate = 0;
/* Check argument count */
if (argc < 2 || argc > 2) {
fprintf(stderr, "Usage: %s sirens_fxt.txt\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Stat file */
if (stat(argv[1], &ss) == -1) {
perror("Stat failed");
exit(EXIT_FAILURE);
}
/* Check it is a file */
if (!S_ISREG(ss.st_mode)) {
fprintf(stderr, "%s must be a valid file\n", argv[1]);
exit(EXIT_FAILURE);
}
/* Check if readable */
if (access(argv[1], R_OK)) {
fprintf(stderr, "%s must be readable\n", argv[1]);
exit(EXIT_FAILURE);
}
/* Open file */
if ((fd = open(argv[1], O_RDONLY)) == -1) {
perror("Open failed");
exit(EXIT_FAILURE);
}
/* Read file */
do {
/* Try reading line and check line format */
if ((ret = read(fd, &line, 10)) == 10 && strspn(line, "0123456789") == 9 && line[9] == '\n') {
/* Terminate key */
line[9] = '\0';
/* Lookup matching leaf */
current = lookupLeaf((void **)&tree, line, 0);
/* Grow unique */
if (current->count == 0) {
unique++;
} else if (current->count == 1) {
unique--;
duplicate++;
}
current->count++;
/* Handle read error */
} else if (ret == -1) {
perror("Read failed");
exit(EXIT_FAILURE);
/* Handle partial read or invalid line format */
} else if (ret > 0) {
line[ret] = '\0';
fprintf(stderr, "%s must be filled with lines matching [0-9]{9}LF format; got \"%s\" instead\n", argv[1], line);
exit(EXIT_FAILURE);
}
/* Read until end of file */
} while (ret != 0);
/* Free tree */
freeTree(tree, 0);
/* Close file */
if (close(fd) == -1) {
perror("Close failed");
exit(EXIT_FAILURE);
}
/* Print result */
printf("Unique%s: %d, duplicate%s: %d\n", (unique > 1 ? "s" : ""), unique, (duplicate > 1 ? "s" : ""), duplicate);
/* Successful end */
exit(EXIT_SUCCESS);
}
/* Recursive freeing of node function */
void freeTree(void *base, unsigned index) {
void *current = base, *next;
if (base != NULL) {
/* Branch level */
if (index < 8) {
/* Free horizontaly */
do {
/* Free children */
if (((struct branch *)current)->child != NULL) {
freeTree(((struct branch *)current)->child, index + 1);
}
/* Free current and switch to next */
next = ((struct branch *)current)->next;
free(current);
current = next;
} while (current != NULL);
/* Leaf level */
} else {
/* Free horizontaly */
do {
/* Free current and switch to next */
next = ((struct leaf *)current)->next;
free(current);
current = next;
} while (current != NULL);
}
}
}
/* Recursive lookup and populate tree function */
struct leaf *lookupLeaf(void **base, const char *key, unsigned index) {
/* Return leaf */
struct leaf *ret;
/* Insert node on empty level */
if (*base == NULL) {
/* Branch level */
if (index < 8) {
if ((*base = malloc(sizeof(struct branch))) == NULL) {
perror("Malloc failed");
exit(EXIT_FAILURE);
}
((struct branch *)*base)->key = key[index] - ZERO_OFFSET;
((struct branch *)*base)->next = NULL;
((struct branch *)*base)->child = NULL;
ret = lookupLeaf(&(((struct branch *)*base)->child), key, index + 1);
/* Leaf level */
} else {
if ((*base = malloc(sizeof(struct leaf))) == NULL) {
perror("Malloc failed");
exit(EXIT_FAILURE);
}
((struct leaf *)*base)->key = key[index] - ZERO_OFFSET;
((struct leaf *)*base)->next = NULL;
((struct leaf *)*base)->count = 0;
ret = (struct leaf *)*base;
}
/* Search horizontaly next node */
} else {
/* Current node for horizontal search */
void *current = *base, *prev;
/* Branch level */
if (index < 8) {
/* Lookup node matching key or allocate it */
while(((struct branch *)current)->key != key[index] - ZERO_OFFSET) {
/* End of horizontal search */
if (((struct branch *)current)->next == NULL) {
/* Backup current in prev */
prev = current;
/* Create new branch with key */
if ((current = malloc(sizeof(struct branch))) == NULL) {
perror("Malloc failed");
exit(EXIT_FAILURE);
}
((struct branch *)current)->key = key[index] - ZERO_OFFSET;
((struct branch *)current)->next = NULL;
((struct branch *)current)->child = NULL;
((struct branch *)prev)->next = (struct branch *)current;
} else {
/* Pass to next one */
current = ((struct branch *)current)->next;
}
}
/* Lookup leaf or branch in next level */
ret = lookupLeaf(&(((struct branch *)current)->child), key, index + 1);
/* Leaf level */
} else {
/* Lookup node matching key or allocate it */
while(((struct leaf *)current)->key != key[index] - ZERO_OFFSET) {
/* End of horizontal search */
if (((struct leaf *)current)->next == NULL) {
/* Backup current in prev */
prev = current;
/* Create new leaf with key */
if ((current = malloc(sizeof(struct leaf))) == NULL) {
perror("Malloc failed");
exit(EXIT_FAILURE);
}
((struct leaf *)current)->key = key[index] - ZERO_OFFSET;
((struct leaf *)current)->next = NULL;
((struct leaf *)current)->count = 0;
((struct leaf *)prev)->next = (struct leaf *)current;
} else {
/* Pass to next one */
current = ((struct leaf *)current)->next;
}
}
/* Found leaf, return it */
ret = (struct leaf *)current;
}
}
/* Return leaf */
return ret;
}