This semester I attended to Object Oriented Programming class for mandatory credits and during my first week I had to solve a problem as a warmup:
Code: Select all
#if __STDC__ != 1
#error Please use standard compliance compiler.
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Employee {
struct EmployeeBasic {
char name[32];
struct EmployeeBasicBirthday {
unsigned short year;
unsigned short month;
unsigned short day;
} birth;
} basic;
enum EmployeeRole { UNKNOWN, OFFICE, PRODUCTION } role;
union EmployeeDetail {
struct {
unsigned short count_workday;
} office;
struct {
unsigned short wage_basic;
unsigned short count_product;
} production;
} detail;
};
static int employee_birthday_sort(void const * a, void const * b);
static void employee_input(size_t * count, struct Employee ** e);
static void employee_output_list(size_t count, struct Employee * e);
static void employee_output_wage_total(size_t count, struct Employee * e);
static void employee_output_birthday_closest(size_t count, struct Employee * e);
int
main(void)
{
size_t count;
struct Employee * list;
employee_input(&count, &list);
qsort(list, count, sizeof(*list), employee_birthday_sort);
employee_output_list(count, list);
employee_output_wage_total(count, list);
employee_output_birthday_closest(count, list);
return EXIT_SUCCESS;
}
static int
employee_birthday_sort(void const * a, void const * b)
{
struct EmployeeBasicBirthday m = ((struct Employee *)a)->basic.birth;
struct EmployeeBasicBirthday n = ((struct Employee *)b)->basic.birth;
unsigned long x = m.year * 10000 + m.month * 100 + m.day;
unsigned long y = n.year * 10000 + n.month * 100 + n.day;
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
}
enum Status { GOOD, BAD };
typedef enum Status (*Callback)(size_t sz, char * bff, void * ud);
static void buffer_scan(size_t sz, char * bff, char const * msg, Callback fn, void * ud);
static enum Status employee_get_count(size_t sz, char * bff, void * ud);
static enum Status employee_get_name(size_t sz, char * bff, void * ud);
static enum Status employee_get_birth_year(size_t sz, char * bff, void * ud);
static enum Status employee_get_birth_month(size_t sz, char * bff, void * ud);
static enum Status employee_get_birth_day(size_t sz, char * bff, void * ud);
static enum Status employee_get_role(size_t sz, char * bff, void * ud);
static enum Status employee_get_detail(size_t sz, char * bff, void * ud);
static void
employee_input(size_t * count, struct Employee ** e)
{
char buffer[32];
size_t i;
*count = 0;
buffer_scan(sizeof(buffer), buffer,
"Number of employees? ",
employee_get_count, count
);
*e = calloc(*count, sizeof(**e));
if (*e == NULL) {
exit(EXIT_FAILURE);
}
for (i = 0; i < *count; i = i + 1) {
fprintf(stdout, "Employee #%u :\n", (unsigned)i + 1);
buffer_scan(sizeof((*e + i)->basic.name), (*e + i)->basic.name,
"Name? ",
employee_get_name, NULL
);
buffer_scan(sizeof(buffer), buffer,
"Birth year? ",
employee_get_birth_year, &(*e + i)->basic.birth.year
);
buffer_scan(sizeof(buffer), buffer,
"Birth month? ",
employee_get_birth_month, &(*e + i)->basic.birth.month
);
buffer_scan(sizeof(buffer), buffer,
"Birth day? ",
employee_get_birth_day, &(*e + i)->basic.birth.day
);
buffer_scan(sizeof(buffer), buffer,
"[O]ffice/[P]roduction? ",
employee_get_role, &(*e + i)->role
);
switch ((*e + i)->role) {
case OFFICE:
buffer_scan(sizeof(buffer), buffer,
"Work days? ",
employee_get_detail,
&(*e + i)->detail.office.count_workday
);
break;
case PRODUCTION:
buffer_scan(sizeof(buffer), buffer,
"Basic wage? ",
employee_get_detail,
&(*e + i)->detail.production.wage_basic
);
buffer_scan(sizeof(buffer), buffer,
"Number of products? ",
employee_get_detail,
&(*e + i)->detail.production.count_product
);
break;
default:
exit(EXIT_FAILURE);
}
}
}
static void
output_space(size_t count)
{
size_t i;
for (i = 0; i < count; i = i + 1) {
fputc(' ', stdout);
}
}
#define FIELD_WIDTH_MAX(x) CHAR_BIT * (int)sizeof(x) / 3
static void
employee_output_list_basic(struct EmployeeBasic b)
{
/*
* Due to buffer_scan() only accepts string ending in "\n\0"
* instead of the normal '\0' there is no points reading
* the last characters.
*/
fprintf(stdout, "%*s %04hu-%02hu-%02hu",
(int)sizeof(b.name) - 2, b.name,
b.birth.year,
b.birth.month,
b.birth.day
);
}
static void
employee_output_list(size_t count, struct Employee * e)
{
size_t i;
for (i = 0; i < count; i = i + 1) {
employee_output_list_basic(e[i].basic);
fputs(" ", stdout);
switch (e[i].role) {
case OFFICE:
fprintf(stdout, "OFFICE %*hu",
FIELD_WIDTH_MAX(e[i].detail.office.count_workday),
e[i].detail.office.count_workday
);
break;
case PRODUCTION:
fputs("PRODUCTION ", stdout);
output_space(FIELD_WIDTH_MAX(e[i].detail.office.count_workday));
fprintf(stdout, " %*hu %*hu",
FIELD_WIDTH_MAX(e[i].detail.production.count_product),
e[i].detail.production.count_product,
FIELD_WIDTH_MAX(e[i].detail.production.wage_basic),
e[i].detail.production.wage_basic
);
break;
default:
exit(EXIT_FAILURE);
}
fputs("\n", stdout);
}
}
static void
employee_output_wage_total(size_t count, struct Employee * e)
{
size_t i;
unsigned long total = 0;
for (i = 0; i < count; i = i + 1) {
switch (e[i].role) {
case OFFICE:
total = total + e[i].detail.office.count_workday * 100000;
break;
case PRODUCTION:
total = total + e[i].detail.production.wage_basic + e[i].detail.production.count_product * 5000;
break;
default:
exit(EXIT_FAILURE);
}
}
fprintf(stdout, "Total wage: %*lu\n", FIELD_WIDTH_MAX(total), total);
}
static void
employee_output_birthday_closest(size_t count, struct Employee * e)
{
int d = INT_MAX;
int * a = calloc(count, sizeof(*a));
size_t i;
if (a == NULL) {
exit(EXIT_FAILURE);
}
for (i = 0; i < count; i = i + 1) {
a[i] = abs(e[i].basic.birth.month * 100 + e[i].basic.birth.day - 225); /* YYYY-02-25 */
if (a[i] < d) {
d = a[i];
}
}
fputs("Employee(s) with their birthday closest to February 25th:\n", stdout);
for (i = 0; i < count; i = i + 1) {
if (a[i] == d) {
employee_output_list_basic(e[i].basic);
fputc('\n', stdout);
}
}
free(a);
}
#undef FIELD_WIDTH_MAX
static void
buffer_scan(size_t sz, char * bff, char const * msg, Callback fn, void * ud)
{
/* Is goto-less without sacrifing the flow possible? */
do_scan:
if (msg != NULL) {
fputs(msg, stdout);
}
fgets(bff, sz, stdin);
if (strchr(bff, '\n') == NULL) {
while (getchar() != '\n');
goto do_scan;
}
if (fn != NULL) {
if (fn(sz, bff, ud) != GOOD) {
goto do_scan;
}
}
}
static enum Status
employee_get_count(size_t sz, char * bff, void * ud)
{
unsigned * c = (unsigned *)ud;
sscanf(bff, "%u", c);
if (*c <= 0) {
return BAD;
} else {
return GOOD;
}
}
static enum Status
employee_get_name(size_t sz, char * bff, void * ud)
{
bff[strcspn(bff, "\n")] = '\0';
return GOOD;
}
static enum Status
employee_get_birth_year(size_t sz, char * bff, void * ud)
{
unsigned short * y = (unsigned short *)ud;
sscanf(bff, "%hu", y);
if (*y <= 9999) {
return GOOD;
} else {
return BAD;
}
}
static enum Status
employee_get_birth_month (size_t sz, char * bff, void * ud)
{
unsigned short * m = (unsigned short *)ud;
sscanf(bff, "%hu", m);
if (*m >= 1 && *m <= 12) {
return GOOD;
} else {
return BAD;
}
}
static enum Status
employee_get_birth_day (size_t sz, char * bff, void * ud)
{
unsigned short * d = (unsigned short *)ud;
sscanf(bff, "%hu", d);
if (*d >= 1 && *d <= 31) {
return GOOD;
} else {
return BAD;
}
}
static enum Status
employee_get_role(size_t sz, char * bff, void * ud)
{
size_t i;
enum EmployeeRole * r = (enum EmployeeRole *)ud;
for (i = 0; i < sz; i = i + 1) {
switch (bff[i]) {
case 'O':
case 'o':
*r = OFFICE;
return GOOD;
case 'P':
case 'p':
*r = PRODUCTION;
return GOOD;
default:
*r = UNKNOWN;
return BAD;
}
}
return BAD;
}
static enum Status
employee_get_detail(size_t sz, char * bff, void * ud)
{
sscanf(bff, "%hu", (unsigned short *)ud);
return GOOD;
}
I know there are a lot of things to improve but suffice to say it has been the greatest week I have ever had since I last touched ANSI C programming