3
\$\begingroup\$

This is my first post on this forum. I have read the guidelines and will try to adhere to them, but I apologize if I miss something. The code I am posting is intended to duplicate the output of "date -u" in the XV6 OS. It does this, with the exception of dates before the 1700s (This wasn't relevant to the task, and the math is a little different, from what I read). My goal with this post is to seek advice on improving the code in terms of safety and performance, with an emphasis on best practices that I can generalize to future C code (I realize this may not be as doable for something intended to run in XV6). Advice on both my code and better techniques for posting here is appreciated!

#include "types.h"
#include "stat.h"
#include "user.h"
#include "date.h"

int check_leap(int year);
void print_day(int year, int month, int day);
void print_month(int month);
int month_key(int month);

int
main(void)
{
  struct rtcdate current_date;
  if(date(&current_date) != 0)
    exit();
  print_day(current_date.year, current_date.month, current_date.day);
  print_month(current_date.month);
  printf(1, " %d",current_date.day);
  printf(1," %d:%d:%d", current_date.hour, current_date.minute, current_date.second);
  printf(1," %d", current_date.year);
  printf(1," UTC\n");
  exit();
}

int 
check_leap(int year)
{
  if(year % 4 == 0)
  {
    if(year % 100 == 0)
    {
      if(year % 400 == 0)
        return 1; //Leap year
    }
  }
  return 0;
}

int
month_key(int month)
{
  int key;
  switch (month)
  {
    case 1: key = 1;
            break;
    case 2: key = 4;
            break;
    case 3: key = 4;
            break;
    case 4: key = 0;
            break;
    case 5: key = 2;
            break;
    case 6: key = 5;
            break;
    case 7: key = 0;
            break;
    case 8: key = 3;
            break;
    case 9: key = 6;
            break;
    case 10: key = 1;
             break;
    case 11: key = 4;
             break;
    case 12: key = 6;
             break;
    default: key = -1;
             break;
  }
  return key;
}

void 
print_day(int year, int month, int day)
{
  int week_day;
  int decade = year % 100;
  int century = year / 100;
  int leap = check_leap(year);
  int key = month_key(month);
  if(key < 0)
  {
    printf(1, "%s", "Invalid value for month!\n");
    exit();
  }

  week_day = decade / 4;
  week_day += day;
  week_day += key;
  if((leap) && ((month == 1) || (month == 2)))
  {
    week_day -= 1;
  }
  switch (century)
  {
    case 17: week_day += 4;
             break;
    case 18: week_day += 2;
             break;
    case 19: break;
    case 20: week_day += 6;
             break;
    default: printf(1, "%s", "Centuries before 1700 or after 2000 not yet supported. \n");
             break;
  }
  week_day += decade;
  week_day = (week_day % 7) - 1;

  if(week_day < 0 || week_day > 6)
  {
    printf(1, "Invalid Day error!"); //TS code- to be removed
  }
  switch(week_day)
  {
    case 0: printf(1, "Sun ");
            break;
    case 1: printf(1, "Mon ");
            break;
    case 2: printf(1, "Tue ");
            break;
    case 3: printf(1, "Wed ");
            break;
    case 4: printf(1, "Thu ");
            break;
    case 5: printf(1, "Fri ");
            break;
    case 6: printf(1, "Sat ");
            break;
    default: printf(1, "Unknown day!");
            break;  
 }
 return;
}

void
print_month(int month)
{
  switch (month)
  {
    case 1: printf(1, "Jan");
            break;
    case 2: printf(1, "Feb");
            break;
    case 3: printf(1, "Mar");
            break;
    case 4: printf(1, "Apr");
            break;
    case 5: printf(1, "May");
            break;
    case 6: printf(1, "Jun");
            break;
    case 7: printf(1, "Jul");
            break;
    case 8: printf(1, "Aug");
            break;
    case 9: printf(1, "Sep");
            break;
    case 10: printf(1, "Oct");
            break;
    case 11: printf(1, "Nov");
            break;
    case 12: printf(1, "Dec");
            break;
    default: printf(1, "Month Error");
            break;
 }
 return;
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Toby Speight OP here has incorrect code for either set of rules for check_leap(). The change from Juliann to Gregorian calendar and it leap year rules only begin in 1582. The new rule were only accepted by the majority of the planet in the 19xx, well past 1700s. The old rules continue is use today in select venues. \$\endgroup\$ Mar 31, 2021 at 21:05

1 Answer 1

3
\$\begingroup\$

XV6 bizarreness

XV6 has made at least one ill-advised decision in forcing a non-standard signature of printf that requires a file descriptor be passed in. Maybe they should have omitted printf entirely and made you use fprintf instead, but anyway: at the least, you should make a stdout constant equal to 1 and use this instead of the numeric literal.

You could actually make #define shims in a header file that centralize the XV6-to-C99 translation, including a definition for fprintf that you can call as you would the standard version, but in fact calls the XV6 printf internally. If you're in a course that forces non-standard C but you still want to learn standard C this would be a sane option.

Also, does returning from main not work? If it does, return instead of exit().

month_key

Re-express this long, continuous switch as a static const lookup array that you index into, after having performed a bounds check. The same is possible for your switch (week_day) and switch (month).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.