#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
size_t find_whitespace(const char *buf) {
size_t i = 0;
for (; buf[i] != '\0' && buf[i] != '\n' && buf[i] != ' '; ++i) { }
return i;
}
size_t find_colon(const char *buf) {
size_t i = 0;
for (; buf[i] != '\0' && buf[i] != ':'; ++i) { }
return i;
}
void *xmalloc(size_t size) {
void *ret = calloc(size, 1);
if (!ret) {
fprintf(stderr, "Allocation error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
return ret;
}
struct passport {
/* Any of these may be NULL */
char *byr;
char *iyr;
char *eyr;
char *hgt;
char *hcl;
char *ecl;
char *pid;
//char *cid; // unnecessary
};
bool validate_passport1(const struct passport *p) {
return p->byr && p->iyr && p->eyr && p->hgt && p->hgt && p->hcl && p->ecl && p->pid;
}
bool validate_byr(const char *byr) {
return byr != NULL && strlen(byr) == 4 && 1920 <= atoi(byr) && atoi(byr) <= 2002;
}
bool validate_iyr(const char *iyr) {
return iyr != NULL && strlen(iyr) == 4 && 2010 <= atoi(iyr) && atoi(iyr) <= 2020;
}
bool validate_eyr(const char *eyr) {
return eyr != NULL && strlen(eyr) == 4 && 2020 <= atoi(eyr) && atoi(eyr) <= 2030;
}
bool validate_hgt(const char *hgt) {
if (hgt == NULL) {
return false;
}
size_t len = strlen(hgt);
if (len == 5) {
if (strcmp(hgt + 3, "cm") != 0) {
return false;
}
unsigned n = atoi(hgt);
return 150 <= n && n <= 193;
}
else if (len == 4) {
if (strcmp(hgt + 2, "in") != 0) {
return false;
}
unsigned n = atoi(hgt);
return 59 <= n && n <= 76;
}
return false;
}
bool validate_hcl(const char *hcl) {
if (hcl == NULL || strlen(hcl) != 7 || hcl[0] != '#') {
return false;
}
for (size_t i = 1; i < 7; ++i) {
if (!isxdigit(hcl[i])) {
return false;
}
}
return true;
}
bool validate_ecl(const char *ecl) {
if (ecl == NULL) {
return false;
}
char *valid[] = { "amb", "blu", "brn", "gry", "grn", "hzl", "oth" };
for (size_t i = 0; i < sizeof valid / sizeof *valid; ++i) {
if (strcmp(ecl, valid[i]) == 0) {
return true;
}
}
return false;
}
bool validate_pid(const char *pid) {
if (pid == NULL || strlen(pid) != 9) {
return false;
}
for (size_t i = 0; i < 9; ++i) {
if (!isdigit(pid[i])) {
return false;
}
}
return true;
}
bool validate_passport2(const struct passport *p) {
return
validate_byr(p->byr) &&
validate_iyr(p->iyr) &&
validate_eyr(p->eyr) &&
validate_hgt(p->hgt) &&
validate_hcl(p->hcl) &&
validate_ecl(p->ecl) &&
validate_pid(p->pid);
}
int main(void) {
size_t valid1 = 0;
size_t valid2 = 0;
struct passport p = { 0, };
while (true) {
char buffer[100] = { 0, };
if (fgets(buffer, (sizeof buffer) - 1, stdin) == NULL) { /* BUFSIZE-1 ensures that the line ends with two null bytes */
if (ferror(stdin)) {
fprintf(stderr, "Read error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
break;
}
size_t bufsize = strlen(buffer);
if (bufsize == 0) {
fprintf(stderr, "Error: empty input line.\n");
return EXIT_FAILURE;
}
if (buffer[bufsize-1] != '\n') {
fprintf(stderr, "Line too long.\n");
return EXIT_FAILURE;
}
if (bufsize == 1) {
/* empty line */
valid1 += validate_passport1(&p);
valid2 += validate_passport2(&p);
free(p.byr);
free(p.iyr);
free(p.eyr);
free(p.hgt);
free(p.hcl);
free(p.ecl);
free(p.pid);
memset(&p, 0, sizeof p);
continue;
}
char *buf = buffer;
while (*buf) { /* this loop relies on buf ending in two null bytes or a space and a null byte */
/* find colon, which gives the end of the key */
size_t k_len = find_colon(buf);
char **entry = NULL;
if (strncmp(buf, "byr", k_len) == 0) {
entry = &p.byr;
}
else if (strncmp(buf, "iyr", k_len) == 0) {
entry = &p.iyr;
}
else if (strncmp(buf, "eyr", k_len) == 0) {
entry = &p.eyr;
}
else if (strncmp(buf, "hgt", k_len) == 0) {
entry = &p.hgt;
}
else if (strncmp(buf, "hcl", k_len) == 0) {
entry = &p.hcl;
}
else if (strncmp(buf, "ecl", k_len) == 0) {
entry = &p.ecl;
}
else if (strncmp(buf, "pid", k_len) == 0) {
entry = &p.pid;
}
/* advance buffer and find space or newline, which ends the value */
buf += k_len + 1;
size_t v_len = find_whitespace(buf);
/* if the key is "interesting", save the value */
if (entry != NULL) {
*entry = xmalloc(v_len+1);
memcpy(*entry, buf, v_len);
}
buf += v_len + 1;
}
}
free(p.byr);
free(p.iyr);
free(p.eyr);
free(p.hgt);
free(p.hcl);
free(p.ecl);
free(p.pid);
printf("%zu\n", valid1);
printf("%zu\n", valid2);
return EXIT_SUCCESS;
}