Coder Social home page Coder Social logo

kilo's Introduction

Kilo

Kilo is a small text editor in less than 1K lines of code (counted with cloc).

A screencast is available here: https://asciinema.org/a/90r2i9bq8po03nazhqtsifksb

Usage: kilo <filename>

Keys:

CTRL-S: Save
CTRL-Q: Quit
CTRL-F: Find string in file (ESC to exit search, arrows to navigate)

Kilo does not depend on any library (not even curses). It uses fairly standard VT100 (and similar terminals) escape sequences. The project is in alpha stage and was written in just a few hours taking code from my other two projects, load81 and linenoise.

People are encouraged to use it as a starting point to write other editors or command line interfaces that are more advanced than the usual REPL style CLI.

Kilo was written by Salvatore Sanfilippo aka antirez and is released under the BSD 2 clause license.

kilo's People

Contributors

antirez avatar dayuoba avatar lpereira avatar property404 avatar skeeto avatar unknowntpo avatar vuonghv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kilo's Issues

How can I debug the program using GDB?

I tried to debug the program using GDB and VS Code, but since the code uses escape sequences on Terminal for display, it can interfere with GDB output. For example, when a program calls write(1, "\x1b[6n" , 4) GDB will just hang there and not continue. How can I debug in this case? Thanks!

Compilation error

dcarol@dcarol ~/w/g/kilo> make
cc -o kilo kilo.c -Wall -W -pedantic -std=c99
In file included from /usr/include/termios.h:25:0,
                 from kilo.c:40:
/usr/include/features.h:148:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]
 # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
   ^~~~~~~
kilo.c: In function ‘editorRefreshScreen’:
kilo.c:953:19: warning: implicit declaration of function ‘time’ [-Wimplicit-function-declaration]
     if (msglen && time(NULL)-E.statusmsg_time < 5)
                   ^~~~

Weird cursor behaviour when non-English characters are typed

When characters like "ä", "ö" or "å" are typed in the editor, the showable cursor shifts 2 times to the right (one time more than it should). However, the characters typed after this will not appear right before the showable cursor, but two or more columns to the left depending on how many non-English characters you have typed. I didn't investigate what was causing this problem.

Use smcup and rmcup to save/restore screen buffer

Vim and others appear to do this. It also has the added benefit of fixing Page Up and Page Down on MacOS using Terminal.app.

void initEditor() {
	write(STDOUT_FILENO, "\033[?47h", 6);    // New (smcup)
	EditorConfig.cursor.x = 0;
	EditorConfig.cursor.y = 0;
	if (getWindowSize(&EditorConfig.screenRows, &EditorConfig.screenCols) == -1) die("getWindowSize");
	terminalClearScreen();
}

// ...

void disableRawMode() {
	write(STDOUT_FILENO, "\033[?47l", 6);    // New (rmcup)
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &EditorConfig.orig_termios);
}

More info here.

Bug in deleting rows.

This is vanilla kilo, with only one patch:

 diff --git a/kilo.c b/kilo.c
 index 9490a77..207e297 100644
 --- a/kilo.c
 +++ b/kilo.c
 @@ -37,6 +37,7 @@
  #define _BSD_SOURCE
  #define _GNU_SOURCE

 +#include <time.h>
  #include <termios.h>
  #include <stdlib.h>
  • Open the attached file rename it t.c beforehand, so that the syntax highlighting is enabled.
  • Delete 10+ lines from the middle.
  • Move to the bottom of the file.
  • Delete a few rows.
  • Segfault.

Backtrace shows that we die with bogus row values:

 Core was generated by `./kilo t.c'.
 Program terminated with signal SIGSEGV, Segmentation fault.
 #0  0x0000000000401462 in editorRowHasOpenComment (row=0x16c5e90) at kilo.c:361
 361 - 41    if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT &&
 (gdb) bt
 #0  0x0000000000401462 in editorRowHasOpenComment (row=0x16c5e90) at kilo.c:361
 #1  0x00000000004015fd in editorUpdateSyntax (row=0x16c5620) at kilo.c:395
 #2  0x0000000000401da7 in editorUpdateRow (row=0x16c5620) at kilo.c:566
 #3  0x00000000004024e5 in editorRowAppendString (row=0x16c5620, 
     s=0x16c4a70 "", len=0) at kilo.c:668
 #4  0x00000000004028ea in editorDelChar () at kilo.c:750
 #5  0x0000000000403d6c in editorProcessKeypress (fd=0) at kilo.c:1197
 #6  0x0000000000403f5c in main (argc=2, argv=0x7fffaf038a38) at kilo.c:1270
 (gdb) p row->size
 $1 = 1819440195
 (gdb) p row->rsize
 $2 = 1025525293
 (gdb) 

Suspect, given the nature of the crash that editorRowDelChar, or editorDelRow are to blame. Either the memcpy is going wrong such that the size become pointers, or something else is corrupting them.

"Video" here for a clearer picture - https://asciinema.org/a/6ou2c1lmofuzedery5mjx7kf3

This is affecting my fork in a different way, but we'll keep that quiet.
t.c.txt

Windows compiling

The terminios.h dependency requires cygwin is there any plan on changing this?

getCursorPosition sometimes fails and sometimes it works but ...

I'm following your blog on building a text editor. btw Thank you so much for that nice blog.

when I call getCursorposition to query the window size it stores the window size to the buf variable noramly but also print the escape sequence to the terminal and waits for me to entire a key before it refresh the screen and display the ~ rows.
terminal example

I tired to clear the screen after reading the window size but it't not working.
I also cloned down your repo to test it and you have the same issue.

my question is, why is that happinging and how to fix it?

Thank you

/usr/include/features.h:185:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]

after git clone and trying to compile it's showing

In file included from /usr/include/termios.h:25,
                 from kilo.c:40:
/usr/include/features.h:185:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]
  185 | # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
      |   ^~~~~~~
kilo.c: In function ‘editorRefreshScreen’:
kilo.c:953:19: warning: implicit declaration of function ‘time’ [-Wimplicit-function-declaration]
  953 |     if (msglen && time(NULL)-E.statusmsg_time < 5)
      |                   ^~~~

Strings in comments and viceversa

Strings in comments & viceversa are incorrectly highlighted.

image

Quick fix might be:

When handling comments check first if we're not in string:
if (!in_string) { /* Handle multi line comments. */

Same for strings:
if (!in_comment) { /* Handle "" and '' */

In editorUpdateSyntax.

Will make a PR if you want but in a few hours.

use on Windows?

This program may not be used on Windows , e.g. terminos.h.
Do you plan to support running on Windows?

Heap buffer overflow in kilo's editor_update_syntax(…)

Hi,

Kilo appears to have a heap buffer overflow triggered by the memcmp call on line 475 of kilo.c:

            int j;
            for (j = 0; keywords[j]; j++) {
                int klen = strlen(keywords[j]);
                int kw2 = keywords[j][klen-1] == '|';
                if (kw2) klen--;

                if (!memcmp(p,keywords[j],klen) &&

The signature of memcmp:

int memcmp(const void *s1, const void *s2, size_t n);

memcmp operates under the assumption that s1 and s2 are at least n bytes long each.

This assumption clearly holds for the second argument (keywords[j]), but there are no checks in place that makes sure that this assumption holds also for the first argument (p).

The heap overflow can be verified by compiling kilo with ASAN enabled:

$ git clone https://github.com/antirez/kilo.git
$ cd kilo
$ clang -fsanitize=address -o kilo kilo.c
$ ./kilo kilo.c
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300000dcd5 at pc 0x0001029f16f9 bp 0x7fff5d233440 sp 0x7fff5d232c00
READ of size 6 at 0x60300000dcd5 thread T0
    #0 0x1029f16f8 in wrap_memcmp (libclang_rt.asan_osx_dynamic.dylib+0x116f8)
    #1 0x1029d08db in editorUpdateSyntax (kilo+0x1000048db)
    #2 0x1029d18fb in editorUpdateRow (kilo+0x1000058fb)
    #3 0x1029d1d86 in editorInsertRow (kilo+0x100005d86)
    #4 0x1029d3c85 in editorOpen (kilo+0x100007c85)
    #5 0x1029d6ae7 in main (kilo+0x10000aae7)
    #6 0x7fff8956c5ac in start (libdyld.dylib+0x35ac)
    #7 0x1  (<unknown module>)

0x60300000dcd5 is located 0 bytes to the right of 21-byte region [0x60300000dcc0,0x60300000dcd5)
allocated by thread T0 here:
    #0 0x102a289c0 in wrap_malloc (libclang_rt.asan_osx_dynamic.dylib+0x489c0)
    #1 0x1029d1303 in editorUpdateRow (kilo+0x100005303)
    #2 0x1029d1d86 in editorInsertRow (kilo+0x100005d86)
    #3 0x1029d3c85 in editorOpen (kilo+0x100007c85)
    #4 0x1029d6ae7 in main (kilo+0x10000aae7)
    #5 0x7fff8956c5ac in start (libdyld.dylib+0x35ac)
    #6 0x1  (<unknown module>)

Small nit

Hey there, I don't really want to make a pull request here so I thought I'd just make an issue. Sorry if that's not proper etiquette! By the way, love the work.
The following lines of code appear in the source code:

erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
if (row) {
        for (j = E.coloff; j < (E.cx+E.coloff); j++) {
            if (j < row->size && row->chars[j] == TAB) cx += 7-((cx)%8);
            cx++;
        }
}

Surely this should be simplified slightly to:

if (filerow < E.numrows) {
        erow *row = &E.row[filerow];
        for (j = E.coloff; j < (E.cx+E.coloff); j++) {
            if (j < row->size && row->chars[j] == TAB) cx += 7-((cx)%8);
            cx++;
        }
 }

It seems a little off to make the conditional based off of the result of another conditional that appears literally one line above.

Seg Fault

When trying to do ./kilo clip.c , I get zsh: segmentation fault kilo clip.c. View clip.c.

macOS 11.2.2, on x86_64 Intel CPU, compiled with clang and gcc (tried both, tried all optimisation levels), using default terminal and zsh.

Will try on linux and update.

EDIT 1 :
Possibly due to one/some of the errors posted in #77 .

Most likely:

kilo.c:1211:9: warning: 'break' will never be executed [-Wunreachable-code-break]
        break;
kilo.c:1307:12: warning: 'return' will never be executed [-Wunreachable-code-return]
    return 0;

or some other UB / EB ?

EDIT 2:
It does not instantly seg fault. It takes quite a few seconds. And it seems to use the CPU highly during this.
it is read and writing Gigabytes of data during this ! WTF

compiler warnings

just for the records:

Fedora release 29

gcc (GCC) 8.2.1 20181215 (Red Hat 8.2.1-6)

In file included from /usr/include/termios.h:25,
from kilo.c:40:
/usr/include/features.h:184:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]

warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"

^~~~~~~
kilo.c: In function ‘editorRefreshScreen’:
kilo.c:953:19: warning: implicit declaration of function ‘time’; did you mean ‘tee’? [-Wimplicit-function-declaration]
if (msglen && time(NULL)-E.statusmsg_time < 5)
^~~~
tee

[Documentation] Tutorial?

Not sure if you ever have the time, but in the distant future perhaps you could either add
to the main README, or to another file, and explain the code as well on a semi-high
level? Just as an entry point and starter, does NOT have to be complete, just something
that gives an overview, and adds a bit more to that overview. Thank you for reading.

License

This project looks ideal for my needs.

The complication is I would like to import it into a project with a GPL License.

This is not a religious argument about licenses, just a practical request. Would it be possible for the original author to give this a dual License?

Thanks.
Jon

‘UINT32_MAX’ undeclared problem in Linux

Problems: UINT32_MAX’ undeclared problem in Linux

Why I post this issue?

I use make to build the program, and found this problem:

$ make
cc -o kilo kilo.c -Wall -W -pedantic -std=c99
kilo.c: In function ‘editorUpdateRow’:
kilo.c:567:21: error: ‘UINT32_MAX’ undeclared (first use in this function); did you mean ‘__UINT32_MAX__’?
     if (allocsize > UINT32_MAX) {
                     ^~~~~~~~~~
                     __UINT32_MAX__
kilo.c:567:21: note: each undeclared identifier is reported only once for each function it appears in
Makefile:4: recipe for target 'kilo' failed
make: *** [kilo] Error 1

My environment:

$ uname -a
Linux beaglebone 4.9.78-ti-r94 #1 SMP PREEMPT Fri Jan 26 21:26:24 UTC 2018 armv7l GNU/Linux

How I fix it ?

Just include <stdint.h> and the program compiled correctly with no errors.

Test kilo on MacOS

I found no erorr when compiling.
And my environment on MacOS is:

$ uname -a
Darwin unknowntpo.local 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64

Syntax Highlighting Segfault

How to reproduce

  1. Create any *.c file with 4 empty lines.
  2. Open the file with kilo, go to the 3rd line, press delete twice
  3. Now 2 lines remain, and cursor is at the first line. Move to next line
  4. Enter any character, e.g., a
  5. Segfault

#81 resolves this, please consider merging it.

Question: Why kilo allocating 9 bytes memory for a non-printable character?

I am reading kilo code for learning C language.
In line551, allocating 9 bytes memory for one non-printable character.
Why 9 bytes memory need for a non-printable character?

And nonprint variable is only initialized to 0 at line 543, and it is not assigned to other value.
So I think nonprint is not useful at line 551.
What's the meaning of this?


row->render = malloc(row->size + tabs*8 + nonprint*9 + 1);

[newbie question] How does editorRefreshScreen work?

Hi. Awesome project!
I always wanted to learn, how does textminal/text magic work, and this code is fantastic. But I can't understand the editorRefreshScreen function. How doest it render text and doesn't just append it to terminal? What is the idea behind it?

Can someone give me a hint?

Use curses to support non-vt100 terminals? (request)

I have an IBM 3161 ASCII terminal. The point of using curses is to make your programs terminal-independent. I think it'd be a very good idea to add it (though I understand this may push the 'lines of code' up a lot).

Image of IBM 3161 ASCII Display Station

If your intention is portability, curses is the way to go. VT100 codes are not universal, although they are common. The 3161 doesn't emulate a VT100 either; it emulates the ADDS Viewpoint, ADM-3A, ADM-5, Hazeltine 1500, IBM 3101, and TeleVideo 910. None of those are VT100 compatible.

Alternately, a method to manually supply alternate escape codes would be good.

Clang Warnings

kilo.c:71:8: warning: padding size of 'struct editorSyntax' with 4 bytes to alignment boundary [-Wpadded]
struct editorSyntax {
       ^
kilo.c:106:11: warning: padding struct 'struct editorConfig' with 4 bytes to align 'filename' [-Wpadded]
    char *filename; /* Currently open filename */

kilo.c:85:11: warning: padding struct 'struct erow' with 4 bytes to align 'chars' [-Wpadded]
    char *chars;        /* Row content. */
          ^
kilo.c:81:16: warning: padding size of 'struct erow' with 4 bytes to alignment boundary [-Wpadded]
typedef struct erow {

kilo.c:229:20: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
                ~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kilo.c:231:20: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    raw.c_oflag &= ~(OPOST);

kilo.c:256:21: warning: implicit conversion loses integer precision: 'ssize_t' (aka 'long') to 'int' [-Wshorten-64-to-32]
    while ((nread = read(fd,&c,1)) == 0);

kilo.c:383:36: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    row->hl = realloc(row->hl,row->rsize);
              ~~~~~~~         ~~~~~^~~~~
kilo.c:485:28: warning: implicit conversion loses integer precision: 'unsigned long' to 'int' [-Wshorten-64-to-32]
                int klen = strlen(keywords[j]);

kilo.c:489:43: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
                if (!memcmp(p,keywords[j],klen) &&

kilo.c:541:26: warning: implicit conversion loses integer precision: 'unsigned long' to 'int' [-Wshorten-64-to-32]
            int patlen = strlen(s->filematch[i]);
                ~~~~~~   ^~~~~~~~~~~~~~~~~~~~~~~
kilo.c:573:31: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    row->render = malloc(row->size + tabs*8 + nonprint*9 + 1);

kilo.c:594:50: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    E.row = realloc(E.row,sizeof(erow)*(E.numrows+1));
                                      ~ ~~~~~~~~~^~
kilo.c:596:64: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
        memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at));

kilo.c:599:22: warning: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'int' [-Wshorten-64-to-32]
    E.row[at].size = len;

kilo.c:627:63: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1));

kilo.c:648:22: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    p = buf = malloc(totlen);

kilo.c:667:57: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
        row->chars = realloc(row->chars,row->size+padlen+2);
                     ~~~~~~~            ~~~~~~~~~~~~~~~~^~
kilo.c:674:50: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
        row->chars = realloc(row->chars,row->size+2);
                     ~~~~~~~            ~~~~~~~~~^~
kilo.c:678:22: warning: implicit conversion loses integer precision: 'int' to 'char' [-Wimplicit-int-conversion]
    row->chars[at] = c;

kilo.c:685:42: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    row->chars = realloc(row->chars,row->size+len+1);

kilo.c:744:63: warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
        editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol);

kilo.c:771:65: warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
        editorRowAppendString(&E.row[filerow-1],row->chars,row->size);

kilo.c:821:40: warning: implicit conversion changes signedness: 'ssize_t' (aka 'long') to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
        editorInsertRow(E.numrows,line,linelen);
        ~~~~~~~~~~~~~~~                ^~~~~~~
kilo.c:839:22: warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
    if (write(fd,buf,len) != len) goto writeerr;
        ~~~~~        ^~~
kilo.c:868:38: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    char *new = realloc(ab->b,ab->len+len);
                ~~~~~~~       ~~~~~~~^~~~
kilo.c:860:8: warning: padding size of 'struct abuf' with 4 bytes to alignment boundary [-Wpadded]
struct abuf {
       ^
kilo.c:939:30: warning: declaration shadows a local variable [-Wshadow]
                        char buf[16];
                             ^
kilo.c:885:10: note: previous declaration is here
    char buf[32];
         ^
kilo.c:996:33: warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
    write(STDOUT_FILENO,ab.b,ab.len);
    ~~~~~                    ~~~^~~
kilo.c:976:18: warning: implicit conversion loses integer precision: 'unsigned long' to 'int' [-Wshorten-64-to-32]
    int msglen = strlen(E.statusmsg);
        ~~~~~~   ^~~~~~~~~~~~~~~~~~~
kilo.c:994:22: warning: implicit conversion loses integer precision: 'unsigned long' to 'int' [-Wshorten-64-to-32]
    abAppend(&ab,buf,strlen(buf));
    ~~~~~~~~         ^~~~~~~~~~~
kilo.c:1005:47: warning: format string is not a string literal [-Wformat-nonliteral]
    vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap);
                                              ^~~
kilo.c:1057:33: warning: implicit conversion loses integer precision: 'int' to 'char' [-Wimplicit-int-conversion]
                query[qlen++] = c;
                              ~ ^
kilo.c:1090:44: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
                    saved_hl = malloc(row->rsize);
                               ~~~~~~ ~~~~~^~~~~
kilo.c:1076:41: warning: implicit conversion loses integer precision: 'long' to 'int' [-Wshorten-64-to-32]
                    match_offset = match-E.row[current].render;
                                 ~ ~~~~~^~~~~~~~~~~~~~~~~~~~~~
kilo.c:1211:9: warning: 'break' will never be executed [-Wunreachable-code-break]
        break;
kilo.c:1307:12: warning: 'return' will never be executed [-Wunreachable-code-return]
    return 0;

[patch]Comment handling assumes 2 characters and unique prefixes.

I've created a fork which pushes a lot of the functionality, including loading syntax highlighting, to external lua script. One obvious thing I wanted to do was to define syntax highlighting for lua, which I've done. Unfortunately this breaks the implicit assumption that has been made regarding single-line vs. multi-line comment handling.

In C/C++ comments are like this:

// Single line.
/* Multi-line */

Each token is distinct; // vs. /* vs */. However in Lua that is not true:

-- Single-line
--[[ Multi
LIne --]]

kilo doesn't support this correctly, because when scanning a line the case of the single-line comment is handled first, which means --[[ Foo is treated as a single-line comment, rather than as the opening of a multi-line comment.

My fork makes this obvious pretty quickly. To demonstrate this in pure-kilo though you'll need to define a new syntax table entry with contents like this:

  char *LUA_HL_extensions[] = {".lua",NULL};
  char *LUA_HL_keywords[] = {
     "and", "break", "do", "else", "elseif", "end", "false","for", "function", "if", "in", "local", "nil", "not","or", "repeat", "return", "then", "true", "until",    "while", NULL
  };

Then add that to the struct editorSyntax HLDB[] definition:

{
    /* Lua */
    LUA_HL_extensions,
    LUA_HL_keywords,
    "--","--[[","--]]",
    HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS
}

Once present the following file test.lua can be used to test:

 function foo()
    -- This is a comment
 end
 --[[ This function should be 100% commented - and isn't
 function bar()
 ..
 end
 --]]

Even though exposed in my fork I believe this is a general bug, which is worthy of being reported here. Apologies for the lack of fix. My initial attempt was to move the handling of single-line comments after the string/multi-line comment handling, but that naive approach didn't work. For the moment I'm pretending the problem doesn't exist ;)

Integer Overflow && heap-buffer-overflow in kilo.c

There is a heap overflow caused by integer overflow in kilo.c.
POC:

python -c "print '\t'*477218598" > ./exp

In command line:

make CC="clang-4.0 -fsanitize=address"
./kilo  ./exp

Output:

=================================================================
==18601==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x608000000077 at pc 0x00000050f641 bp 0x7ffd0126fe50 sp 0x7ffd0126fe48
WRITE of size 1 at 0x608000000077 thread T0
    #0 0x50f640  (/home/kirin/kilo/kilo+0x50f640)
    #1 0x50fde0  (/home/kirin/kilo/kilo+0x50fde0)
    #2 0x511ae0  (/home/kirin/kilo/kilo+0x511ae0)
    #3 0x514833  (/home/kirin/kilo/kilo+0x514833)
    #4 0x7f99a53a0b96  (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #5 0x41c339  (/home/kirin/kilo/kilo+0x41c339)

0x608000000077 is located 0 bytes to the right of 87-byte region [0x608000000020,0x608000000077)
allocated by thread T0 here:
    #0 0x4d1990  (/home/kirin/kilo/kilo+0x4d1990)
    #1 0x50f45e  (/home/kirin/kilo/kilo+0x50f45e)
    #2 0x50fde0  (/home/kirin/kilo/kilo+0x50fde0)
    #3 0x511ae0  (/home/kirin/kilo/kilo+0x511ae0)
    #4 0x514833  (/home/kirin/kilo/kilo+0x514833)
    #5 0x7f99a53a0b96  (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/kirin/kilo/kilo+0x50f640) 
Shadow bytes around the buggy address:
  0x0c107fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c107fff8000: fa fa fa fa 00 00 00 00 00 00 00 00 00 00[07]fa
  0x0c107fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==18601==ABORTING

Analyze:
There is an integer overflow in function editorUpdateRow:

    for (j = 0; j < row->size; j++)
        if (row->chars[j] == TAB) tabs++;

    row->render = malloc(row->size + tabs*8 + nonprint*9 + 1);
    idx = 0;
    for (j = 0; j < row->size; j++) {
        if (row->chars[j] == TAB) {
            row->render[idx++] = ' ';
......

The space size being malloc will be calculated based on the number of TABs in one row.
When the number of TAB is too big,it will lead to Integer Overflow. And it will lead to heap-buffer-overflow finally.

Find only finds the first match on a row.

Given the following text:

        erow *row = &E.row[i];
        free(row->chars);

Running find with the search-term row only matches the first occurrence of row in the first line, such that pressing right moves to the second line, rather than the second match.

Not a huge deal, but might be nice to make a note of it.

Fixing is easy enough, but involves iterating over every possible character in the line, and testing that position. Something like:

for (x = 0; x < E.row[current].rsize; x++ ) {
    match = strstr(E.row[current].render + x , query);
    if ( match == 0 ) {
       // got a match at column-position "x"
    }
 }

However this will be grossly inefficient.

Why does it work

I get an error when kilo is compiled, but why does it work. Why is that?
The compiler I are using is Clang

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.