Skip to content

Instantly share code, notes, and snippets.

@paigeruten
Created March 24, 2017 22:46
Show Gist options
  • Save paigeruten/88d4d5ed534edd66edcbc529a62e087a to your computer and use it in GitHub Desktop.
Save paigeruten/88d4d5ed534edd66edcbc529a62e087a to your computer and use it in GitHub Desktop.
  1. Create a new file named kilo.c.
  2. Write a main() function that takes no arguments and returns 0.
  3. Create a Makefile that compiles kilo.c to kilo when you type make.
    • Use $(CC) for the compiler.
    • Turn on warnings using the -Wall -Wextra -pedantic options.
    • Turn on C99 mode using the -std=c99 option.
  4. In main(), Declare a char variable named c.
  5. Include <unistd.h>.
  6. Read a single byte from standard input into c repeatedly using read(), until read() returns an error or reaches the end of file.
  7. Stop the read() loop as soon as c contains the character 'q'.
  8. Create an enableRawMode() function above main() that takes no arguments and doesn't return anything.
  9. Call enableRawMode() at the top of main().
  10. Include <termios.h>.
  11. In enableRawMode(), declare a struct termios variable named raw.
  12. Call tcgetattr() to load terminal attributes into raw.
  13. Turn off the ECHO bit in the c_lflag field of raw.
  14. Call tcsetattr() to apply the attributes in raw back to the terminal. (Use TCSAFLUSH for the second argument.)
  15. Declare a global struct termios variable named orig_termios, above enableRawMode().
  16. Change the tcgetattr() call to load the orig_termios variable instead of raw.
  17. Move the declaration of raw after the tcgetattr() call, and initialize it to the value of orig_termios.
  18. Create a disableRawMode() function above enableRawMode() that takes no arguments and doesn't return anything.
  19. In disableRawMode(), call tcsetattr() to set the terminal attributes to the original ones saved in orig_termios.
  20. Include <stdlib.h>.
  21. In enableRawMode(), after tcgetattr(), use atexit() to register disableRawMode() to be called when the program exits.
  22. Turn off the ICANON bit in the c_lflag field of raw.
  23. Include <ctype.h>.
  24. Include <stdio.h>.
  25. In the read() loop in main(), use isprint() to check if c is printable. If it is, use printf() to print it out as both an ASCII code in decimal, and as a character. If it's not, then just print out its ASCII code.
  26. Turn off the ISIG bit in the c_lflag field of raw.
  27. Turn off the IXON bit in the c_iflag field of raw.
  28. Turn off the ICRNL bit in the c_iflag field of raw.
  29. Turn off the OPOST bit in the c_oflag field of raw.
  30. If your printf() statements print \n at the end, change them to print \r\n instead.
  31. Turn off the IEXTEN bit in the c_lflag field of raw.
  32. Turn off the BRKINT, INPCK, and ISTRIP bits in the c_iflag field of raw.
  33. Set the CS8 bitmask in the c_cflag field of raw.
  34. Set raw.c_cc[VMIN] to 0.
  35. Set raw.c_cc[VTIME] to 1.
  36. In main(), ignore the return value of read().
  37. At the beginning of the loop in main(), set c to the nul character before the read() call.
  38. Create a die() function above disableRawMode() that takes a string named s and doesn't return anything.
  39. In die(), call perror() and pass s to it.
  40. At the end of die(), call exit() with a non-zero exit status.
  41. For each of the 3 calls to tcgetattr() or tcsetattr(), check if the return value is -1, and if so, call die() passing it the name of the function that failed (either "tcgetattr" or "tcsetattr").
  42. Check if the read() call returns -1, and if so, call die() passing it the name of the read() function.
  43. We'll split up the file into sections so we can talk about it easier. A section begins with a comment that looks like /*** name of section ***/ on its own line. At the top of the file, add an /*** includes ***/ section.
  44. Above the orig_termios declaration, add a /*** data ***/ section.
  45. Above the die() function, add a /*** terminal ***/ section.
  46. Above the main() function, add an /*** init ***/ section.
  47. Above the /*** data ***/ section, add an /*** defines ***/ section.
  48. In the /*** defines ***/ section, define a macro named CTRL_KEY that takes one argument, and sets all the bits in the given value to 0 except for the lowest 5 bits.
  49. Instead of breaking out of the main() loop when c is equal to 'q', break out of the loop when c is equal to CTRL_KEY('q').
  50. Create an editorReadKey() function below enableRawMode() that takes no arguments and returns a char.
  51. Move the c variable and the read() call from main() into the new editorReadKey() function.
  52. Have editorReadKey() keep calling read() until read() returns 1, then return the character that it read.
  53. Above the /*** init ***/ section, add an /*** input ***/ section.
  54. In the /*** input ***/ section, create an editorProcessKeypress() function that takes no arguments and doesn't return anything.
  55. In editorProcessKeypress(), call editorReadKey() and store the result in a char named c.
  56. Use a switch statement to call exit() with a 0 exit status when c is equal to CTRL_KEY('q').
  57. In main(), replace the loop with an infinite loop that just calls editorProcessKeypress() repeatedly.
  58. Above the /*** input ***/ section, add an /*** output ***/ section.
  59. In the /*** output ***/ section, create an editorRefreshScreen() function that takes no arguments and doesn't return anything.
  60. In editorRefreshScreen(), use write() to write the escape sequence "\x1b[2J" to standard output.
  61. At the top of the main() loop, call editorRefreshScreen().
  62. At the end of editorRefreshScreen(), use write() to write the escape sequence "\x1b[H" to standard output.
  63. Copy and paste those two write() calls to the top of the die() function.
  64. Copy and paste those same two write() calls to the top of the CTRL_KEY('q') case of the switch statement in editorProcessKeypress().
  65. Above editorRefreshScreen(), create an editorDrawRows() function that takes no arguments and doesn't return anything.
  66. In editorDrawRows(), declare an int named y and have it loop through the values 0 to 23 inclusive.
  67. For each value of y, use write() to write the string "~\r\n" to standard output.
  68. At the end of editorRefreshScreen(), call editorDrawRows().
  69. At the end of editorRefreshScreen(), use write() to write the escape sequence "\x1b[H" to standard output.
  70. At the top of the /*** data ***/ section, define a struct type named editorConfig, and move the orig_termios variable into it.
  71. Below the editorConfig struct, declare a global struct editorConfig variable named E.
  72. In disableRawMode() and enableRawMode(), replace the 3 occurrences of orig_termios with E.orig_termios.
  73. Below editorReadKey(), create a getWindowSize() function that takes two int pointers named rows and cols as arguments, and returns an int.
  74. Include <sys/ioctl.h>.
  75. In getWindowSize(), declare a struct winsize variable named ws.
  76. Call ioctl() with the TIOCGWINSZ request to load the ws variable with the window dimensions.
  77. If ioctl() returns -1, or if the ws_col field of ws is 0, then return -1 to indicate an error occurred. Otherwise, set the rows and cols references to the ws_row and ws_col fields of ws, and return 0 to indicate success.
  78. At the top of the editorConfig struct, add two int fields named screenrows and screencols.
  79. Above main(), create an initEditor() function that takes no arguments and doesn't return anything.
  80. In initEditor(), call getWindowSize(), passing the screenrows and screencols fields of E by reference. If it returns -1, then call die() with the name of the getWindowSize() function.
  81. In main(), call initEditor() after the enableRawMode() call.
  82. In editorDrawRows(), use E.screenrows as the (exclusive) upper bound for y, instead of the (inclusive) 23 we hardcoded earlier.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment