#ifdef COSMOPOLITAN #include "cosmopolitan.h" #else #include #include #endif #include "c-colours/colours.h" #define READ_BUFFER_SIZE 200 /* * Basic Settings */ int rainbow_len = 90; // how many characters long is one full rainbow (360deg) int col_change = -1; // how many degrees between colours in the rainbow int col_dist = -1; // how many characters per colour in output int frequency = -1; // how many degrees to rotate start_hue point on new line struct colour* colours; // the rainbow int num_colours; // the length of the rainbow // start_hue and end_hue hue values int start_hue = -1; int end_hue = -1; bool reverse = false; char help_text[] = "\nUsage: colcat [args] [--] [FILES] \n" "\n" "Concatenate files (or standard input) and print on the standard\n" "output, with a rainbow colour. The colour range and repeat\n" "distance can be specified.\n\n" "Args:\n\n" " -h Print help text and exit\n" " -w Width, in chars, of the rainbow sequence\n" " -r The amount the hue is rotated on new lines\n" " -s The hue to start the rainbow at\n" " -e The hue to end the rainbow at\n" "\n"; /* Print the nth colour of the chars long rainbow */ void print_next_colour(int current) { if (current % (num_colours) == 0 && current > 0) { reverse = !reverse; } int index = 0; if (!reverse) { index = (current % num_colours) / col_dist; } else { index = num_colours -1- (current % num_colours) / col_dist; } if (current % col_dist == 0) { struct colour c = colours[index]; printf("\x1b[38;2;%d;%d;%dm", c.r, c.g, c.b); } } /* Checks if a car is a possible ANSI escape code end_hueing */ bool end_hue_ansi_code(char c) { char codes[] = "HfABCDsuJKmhlP"; for (int i = 0; i < strlen(codes); i++) { if (c == codes[i]) { return true; } } return false; } /* Print a file with rainbow */ int display(FILE *file) { char c = fgetc(file); int start = 0; int end; int counter = 0; if (frequency) end = (num_colours / frequency); else { end = 0; counter = num_colours; // so that print starts at reverse=true } bool in_escape = false; bool in_utf8_mbchar = false; bool vert_reverse = false; while (c != EOF) { if (c == '\n') { if (frequency && end) { if (counter % (end) == 0) { vert_reverse = !vert_reverse; } counter = start += (vert_reverse ? -1 : 1) * frequency; if (counter < 0) { counter = -counter; } } else { counter = num_colours; } reverse = false; } if ((c & 0b11000000) == 0b10000000) { in_utf8_mbchar = true; } else { in_utf8_mbchar = false; } if (c == '\033') { in_escape = true; } else if (end_hue_ansi_code(c) && in_escape) { in_escape = false; putc(c, stdout); c = fgetc(file); counter++; continue; } if (!in_escape && !in_utf8_mbchar) { print_next_colour(counter); } counter++; putc(c, stdout); c = fgetc(file); } return 0; } /* Attempt to open a file and display, or return 1 on error */ int open_and_cat(char *file_name) { FILE *file = fopen(file_name, "r"); if (!file) { return 1; } return display(file); } /* - Generate an array containing all the colours to use for the * rainbow by rotating HSV(0, 100, 100) through hue 0 to 360, using * intervals fitting to how many characters are in the interval. * - They are stored as RGB. * - sets all the relevant global variables for printing the * rainbow out again */ int generate_rainbow() { if (start_hue == -1) { start_hue = 0; } if (end_hue == -1) { end_hue = 360; } if (end_hue < start_hue) { exit(1); } int arclen = end_hue - start_hue; if (rainbow_len < arclen) { num_colours = rainbow_len; col_change = arclen / num_colours; if (col_change == 0) { col_change = 1; } } else { num_colours = arclen; col_change = 1; } col_dist = (rainbow_len) / (num_colours); colours = calloc(num_colours, sizeof(struct colour)); struct colour c; c.h = start_hue; c.s = 1; c.v = 1; c.sp = CS_HSV; for (int i = 0; i < num_colours; i++) { c.h = (int)(c.h + col_change) % arclen; colours[i] = get_rgb(c); } c.h = end_hue; colours[num_colours - 1] = get_rgb(c); return 0; } /* Parses args, sets globals, and chooses whether to read files or stdin */ int main(int argc, char** argv) { int opt; int optcount = 0; while ((opt = getopt(argc, argv, "hw:r:s:e:")) != -1) { optcount++; if (opt == 'w') { optcount++; rainbow_len = atoi(optarg); } else if (opt == 'r') { optcount++; frequency = atoi(optarg); } else if (opt == 's') { optcount++; start_hue = atoi(optarg); } else if (opt == 'e') { optcount++; end_hue = atoi(optarg); } else if (opt == 'h') { printf("%s", help_text); exit(0); } else { printf("%s", help_text); exit(1); } } generate_rainbow(); if (frequency == -1) { frequency = col_change; } if (argc > optcount + 1) { for (int i = optcount + 1; i < argc; i++) { int err = open_and_cat(argv[i]); if (err) { perror("Unable to open file"); exit(err); } } } else { display(stdin); } }