5
\$\begingroup\$

This is a simple C clone of the website hackertyper.net. The code can be found on Github and is reproduced below:

Makefile

PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
DATAROOTDIR=$(PREFIX)/share
DATADIR=$(DATAROOTDIR)
MAN1DIR=$(DATAROOTDIR)/man/man1

CFLAGS=-Wall -pedantic -std=c99
LDFLAGS=-lncurses

all: hackertyper

.PHONY: all clean install

hackertyper: hackertyper.o
    cc -o $@ $^ $(LDFLAGS)
hackertyper.o: src/hackertyper.c
    cc -c $(CFLAGS) -o $@ $^
src/hackertyper.c: src/hackertyper.h
src/hackertyper.h:src/hackertyper.h.in
    sed "s@%datadir%@$(DATADIR)@g" src/hackertyper.h.in > src/hackertyper.h

clean:
    rm -f hackertyper{,.o} src/hackertyper.h

install:
    install -D -m644 data/hackertyper.txt $(DATADIR)/hackertyper.txt
    install -D -m644 man/hackertyper.1 $(MAN1DIR)/hackertyper.1
    install -D -m755 hackertyper $(BINDIR)/hackertyper

src/hackertyper.c

#include "hackertyper.h"

char* filename;
FILE* file;


char chars_per_nl = 1;

int main(int argc, char* argv[]) {
  parse_args(argc, argv);

  if(open_file(filename) == -1){

    return -1;
  }

  init();

  int clear_msg_flag = 0;

  nc_color_green();

  int running = 1;

  while(running) {
    // buffer here so that buffering happens before clear_msg
    int input_ch  = getch();  
    // message was drawn last time and we need to clear it
    if(clear_msg_flag) {
      clear_msg_flag = 0;
      clear_msg();
    }

    switch(input_ch){
    //C-c
    case 3:
      running = 0;
      break;
    //C-d
    case 4:
      clear();
      nc_color_red();
      draw_msg("ACCESS DENIED");
      clear_msg_flag = 1;
      break;
    // C-g
    case 7:
      clear();
      nc_color_green();
      draw_msg("ACCESS GRANTED");

      clear_msg_flag = 1;

      break;
    case KEY_BACKSPACE:
      backspace();
      break;
    default:
      for(int i = 0; i < 5; i++) {
        int output_ch = fgetc(file);

        if(output_ch == EOF) {
          rewind(file);

          output_ch = fgetc(file);
        }

        if(output_ch != '\r')
          addch(output_ch);
      }

      refresh();
      break;
    }
  }

  end();
}

void parse_args(int argc, char* argv[]) {
  if(argc > 1) {
    for(int i = 0; i < argc; i++) {
      if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
        printf(HELP_TEXT);

        exit(0);
      }

      if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
        printf(VERSION_TEXT);

        exit(0);
      }

      if(strcmp(argv[i], "-f") == 0) {
        if(i+1 >= argc) {
          fprintf(stderr, HELP_TEXT);

          exit(-1);
        }

        filename = argv[i+1];
      }
    }
  }
}

int open_file(char* filename) {
  filename = filename ? filename : default_filename;
  file     = fopen(filename, "r");

  return file == NULL ? -1 : 0;
}

void init(){
  initscr();
  raw();
  noecho();
  scrollok(stdscr, true);
  keypad(stdscr, true);

  // check line endings
  // TODO: expand this to work with endings besides \n and \r\n

  char ch;

  while(ch !='\n' && ch !='\r'){
    ch = fgetc(file);
  }

  if (ch == '\r') {
    chars_per_nl = 2;
  }

  rewind(file); 

  if(has_colors()){
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);
    init_pair(2, COLOR_RED, COLOR_BLACK);
  }
}

void nc_color_green(){
  if(has_colors()){
    attroff(COLOR_PAIR(2));
    attron(COLOR_PAIR(1));
  }
}

void nc_color_red(){
  if(has_colors()){
    attroff(COLOR_PAIR(1));
    attron(COLOR_PAIR(2));
  }
}

void nc_color_default(){
  if(has_colors()){
    attroff(COLOR_PAIR(1));
    attroff(COLOR_PAIR(2));
  }
}

void backspace(){
  fseek(file,-1 ,SEEK_CUR);

  int x,y;
  getyx(stdscr,y,x);

  if(x == 0) {

    if( y == 0 ) {

      return;
    }

    x = getmaxx(stdscr);
    // set x to x minus 1
    move(--y,--x);

    char ch = ' ';

    while(ch == ' ' && x != 0){
      move(y,--x);
      ch=inch();
    }

    fseek(file, -chars_per_nl, SEEK_CUR);
  } else {
    move(y,x-1);

  }


  delch();
}

void draw_msg(char* msg) {
  int len             = strlen(msg);
  unsigned char hash  = '#';
  unsigned char space = ' ';

  int w;
  int h;

  getmaxyx(stdscr, h, w);

  move(h/2 - 2, w/2 - len/2 - 3);

  for(int i = 0; i < len + 6; i ++)
    addch(hash);

  move(h/2 - 1, w/2 - len/2 - 3);

  addch(hash);

  for(int i = 0; i < len + 4; i ++)
    addch(space);

  addch(hash);

  move(h/2, w/2 - len/2 - 3);
  printw("#  %s  #", msg);

  move(h/2 + 1, w/2 - len/2 - 3);
  addch(hash);

  for(int i = 0; i < len + 4; i ++)
    addch(space);

  addch(hash);

  move(h/2 + 2, w/2 - len/2 - 3);

  for(int i = 0; i < len + 6; i ++)
    addch(hash);
}

void clear_msg(){
      nc_color_green();
      clear();
      move(0, 0);
      // seek file to next line
      int ch = 0;

      while(ch != '\n'){
        ch = fgetc(file);
      }
}

void end(){
  endwin();
  fclose(file);
}

src/hackertyper.h.in

#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <string.h>

#define default_filename "%datadir%/hackertyper.txt"

#define HELP_TEXT "\
Usage: hackertyper [-f file] [-h] [-v]\n\
Print text from file to stdout on pressing keys, similar to behavior of website https://www.hackertyper.org/\n\
\n\
Options:\n\
  -f FILE, --filename FILE  Print text from FILE rather than from default file\n\
  -h, --help                Print this help string\n\
  -v, --version             Print version information\n\
"

#define VERSION_TEXT "\
hackertyper 2.1\n\
Copyright (C) 2019 Lani Willrich\n\
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>\n\
This is free software: you are free to change and redistribute it.\n\
There is NO WARRANTY, to the extent permitted by law.\n\
Written by Lani Willrich\n\
"

void parse_args(int argc,char* argv[]);

int open_file(char* filename);

void init();
void nc_color_red();
void nc_color_green();
void nc_color_default();

void backspace();

void draw_msg(char* msg);
void clear_msg();

void end();

Some irrelevant files have not been listed including an auto-generated man page and the data files.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.