Skip to content

Commit

Permalink
Userland: Add support for printing multiple columns to the cal command
Browse files Browse the repository at this point in the history
Now cal is able to print the entire year when only that is passed
as an argument. For example: `cal 1992`.

However this meant breaking the highlighted day escape sequence
as it messed up the layout and the character count for each of the
rows :(

Now the current day is specified like 17* (for example for day 17).
  • Loading branch information
zlotny authored and awesomekling committed Dec 24, 2019
1 parent d058a93 commit 5f9c408
Showing 1 changed file with 141 additions and 16 deletions.
157 changes: 141 additions & 16 deletions Userland/cal.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
#include <LibCore/CArgsParser.h>
#include <stdio.h>
#include <time.h>

const int line_width = 70;
const int line_count = 8;
const int column_width = 22;

char print_buffer[line_width * line_count];
char temp_buffer[line_width * 8];

int target_year;
int target_month;
int target_day;

int current_year;
int current_month;

int day_of_week(int day, int month, int year)
{
static const int seek_table[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
Expand All @@ -21,29 +36,139 @@ int get_number_of_days(int month, int year)
return is_long_month ? 31 : 30;
}

void append_to_print(char* buffer, int row, int column, char* text)
{
int starting_point = (line_width * row) + (column * column_width);
for (int i = 0; text[i] != '\0'; i++) {
buffer[starting_point + i] = text[i];
}
}

void insert_month_to_print(int column, int month, int year)
{
int printing_column = column;
int printing_row = 0;

// FIXME: Both the month name and month header text should be provided by a locale
sprintf(temp_buffer, " %02u - %04u ", month, year);
append_to_print(print_buffer, printing_row, printing_column, temp_buffer);
printing_row++;

sprintf(temp_buffer, "Su Mo Tu We Th Fr Sa");
append_to_print(print_buffer, printing_row, printing_column, temp_buffer);
printing_row++;

int day_to_print = 1;
int first_day_of_week_for_month = day_of_week(1, month, year);
int days_in_the_month = get_number_of_days(month, year);
int last_written_chars = 0;
for (int i = 1; day_to_print <= days_in_the_month; ++i) {
if (i - 1 < first_day_of_week_for_month) {
last_written_chars += sprintf(temp_buffer + last_written_chars, " ");
} else {
if (year == current_year && month == current_month && target_day == day_to_print) {
// FIXME: To replicate Unix cal it would be better to use "\x1b[30;47m%2d\x1b[0m " in here instead of *.
// However, doing that messes up the layout.
last_written_chars += sprintf(temp_buffer + last_written_chars, "%2d*", day_to_print);
} else {
last_written_chars += sprintf(temp_buffer + last_written_chars, "%2d ", day_to_print);
}
day_to_print++;
}

append_to_print(print_buffer, printing_row, printing_column, temp_buffer);

if (i % 7 == 0) {
printing_row++;
memset(temp_buffer, ' ', line_width * 8);
temp_buffer[line_width * 8 - 1] = '\0';
last_written_chars = 0;
}
}
}

void clean_buffers()
{
for (int i = 1; i < line_width * line_count; ++i) {
print_buffer[i - 1] = i % line_width == 0 ? '\n' : ' ';
}
print_buffer[line_width * line_count - 1] = '\0';

for (int i = 0; i < line_width; ++i) {
temp_buffer[i] = ' ';
}
temp_buffer[line_width - 1] = '\0';
}

int main(int argc, char** argv)
{
(void)argc;
(void)argv;
CArgsParser args_parser("cal");
// FIXME: This i a bit of a cheat, as no nested optional args are available on CArgsParser
args_parser.add_single_value("[[day] month] year");

CArgsParserResult args = args_parser.parse(argc, argv);
Vector<String> values = args.get_single_values();

if (values.size() > 3) {
printf("Invalid number of values\n");
args_parser.print_usage();
return 0;
}

time_t now = time(nullptr);
auto* tm = localtime(&now);
int target_day = tm->tm_mday;
int target_month = tm->tm_mon + 1;
int target_year = tm->tm_year + 1900;
int target_day_of_week = day_of_week(1, target_month, target_year);

printf(" %02u - %04u \n", target_month, target_year);
printf("Su Mo Tu We Th Fr Sa\n");
target_year = tm->tm_year + 1900;
target_month = tm->tm_mon + 1;
target_day = tm->tm_mday;

for (int i = 1; i <= get_number_of_days(target_month, target_year); ++i) {
if (i < target_day_of_week) {
printf(" ");
} else {
printf(i != target_day ? "%2d" : "\x1b[30;47m%2d\x1b[0m", i);
}
current_year = target_year;
current_month = target_month;

bool year_mode = false;
switch (values.size()) {
case 3:
target_day = atoi(values[0].characters());
target_month = atoi(values[1].characters());
target_year = atoi(values[2].characters());

printf(i % 7 == 0 ? "\n" : " ");
// When passing the 3 parameters (day, month and year) we assume we're there.
current_year = target_year;
current_month = target_month;
break;
case 2:
target_month = atoi(values[0].characters());
target_year = atoi(values[1].characters());
break;
case 1:
target_year = atoi(values[0].characters());
year_mode = true;
break;
default:
break;
}
printf("\n\n");

clean_buffers();

if (year_mode) {
printf(" ");
printf("Year %4d", target_year);
printf(" \n\n");

for (int i = 1; i < 12; ++i) {
insert_month_to_print(0, i++, target_year);
insert_month_to_print(1, i++, target_year);
insert_month_to_print(2, i, target_year);
printf(print_buffer);
printf("\n");
clean_buffers();
}
} else {
insert_month_to_print(0, target_month, target_year);
printf(print_buffer);
printf("\n\n");
clean_buffers();
}

return 0;
}

0 comments on commit 5f9c408

Please sign in to comment.