Thread: Text editordisplay

  1. #1
    Registered User
    Join Date
    Aug 2009
    Posts
    198

    Text editordisplay

    I made a completely functional text editor with ncurses using a doubly linked list of text lines as its data model. I just wonder how do I implement scrolling and compensation for line wrapping, preferably without iterating through all the lines.

    EDIT: I see that I forgot a space in the title, but I can't edit it.

  2. #2
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    For scrolling, for example the page down key, just make the top line either the current last line (if you want to scroll with 1 line overlap) or the next line after the current last line to be the top line, then re-drive your display loop.

    For line at a time scrolling, I would use the arrow keys. For example, if the current cursor-line is the top displayed line, and you sense the arrow up key, then make the current-top-line minus 1 (Well, make it MAX(CURRENT-TOP-LINE-1,0)), and re-drive your display loop. If ncurses allows the typematic effect, just keep doing it.
    Mainframe assembler programmer by trade. C coder when I can.

  3. #3
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    I already did it and to completely avoid the possibility of a line taking up more than a line when viewed I made the current line scroll horizontally, with indicator characters ('<' and '>' in reverse video) to show that there is more to be seen past the edge.

    To avoid iterating the whole text, I made the print function take a parameter that says what line the linked-list element it was given is on.

    The display works great, but the problem is that on occasion is segfaults.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MTK View Post
    The display works great, but the problem is that on occasion is segfaults.
    Congratulations MTK! (you managed that quicker than I would have guessed)

    I'm sure it is tempting to blame ncurses at this point, but the three apps I use the most are FF, mc, and vim. The last two are both ncurses based and never seg fault.

    So it's probably just an overflow somewhere, just keep re-reading thru your code, think about "off by one" errors because they really are the most common IMO, and maybe try and catch a fault in gdb if you can get it to happen often enough.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    I wasn't saying it's ncurses's fault.

    It is probably some small thing in the memory management code.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MTK View Post
    I wasn't saying it's ncurses's fault.

    It is probably some small thing in the memory management code.
    I didn't say you did! We may be right! (good luck)
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    MTK, do you not have a debugger?
    Mainframe assembler programmer by trade. C coder when I can.

  8. #8
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    Yes, my distro came with gdb (GNU DeBugger).

    Is it possible to run my program in gdb and after a segfault to get a stack trace and examine variable contents?

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MTK View Post
    Yes, my distro came with gdb (GNU DeBugger).

    Is it possible to run my program in gdb and after a segfault to get a stack trace and examine variable contents?
    gdb does not work perfectly with some APIs, I dunno about nucurses, but it is worth trying.

    If the seg fault is happening in a predictable way, run it under gdb until the fault occurs, and examine the output. If you are lucky, it will point right to the problem.

    If not, it will lay the blame somewhere in the ncurses library. This is because you overflowed and "nicked" it. However, consider that probably the two regions in memory are part of the same call stack. So if you can figure out which ncurses function went wrong, look around in the calling function (or the function that called that function) and you may be able to find the guilty variable near-by. The headache occurs when the function pointed to by gdb is internal to ncurses (eg, not something from your own code), in which case you may be confronting the daunting task of trying to figure out which ncurses routine that you used, used that indicated routine internally (oh joy).

    You can also step thru, line by line, using breakpoints, if the fault is predictable enough (eg, you can replicate it easily).
    Last edited by MK27; 09-12-2009 at 12:16 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    The segfault only happens when I rapidly and randomly hit characters, the Enter key, and the arrow keys at the same time. If I do this there should be a segfault after a second or two.

    I heard that gdb has a tty command so it can direct the program's I/O to a different terminal.

  11. #11
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    One thing you can do with GDB is attach onto an already-running process. So you can launch your program, then figure out its processid and attach onto it with something like this (presumably in a different terminal):
    Code:
    $ ps aux | grep program
    ... <some PID> ...
    $ gdb ./program <some-PID>
    That way your program runs as normal (after you type "continue", of course), but when it segfaults, you just switch terminals to GDB and see what happened.

    Is it possible to run my program in gdb and after a segfault to get a stack trace and examine variable contents?
    Absolutely -- "bt" or "backtrace" will give you your backtrace. You can type "up" and "down" or "frame N" to switch to a different function in the stack trace. And "print <something>" lets you print any C expression, so you can look at a variable with "print variable" or "p variable".

    [edit] You can also redirect your program's output as you said, but I think the method I described is more likely to work. [/edit]
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  12. #12
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    So I assume that the program must be compiled with debug info, right?

    And how do I find out its PID?

  13. #13
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I showed you. Sort of. Use "ps aux".

    Here's a real example.

    I'll compile and run the code to make sure it works. Notice that I compile with debugging information (-g).
    Code:
    $ cat greet.c
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char name[BUFSIZ], *p;
    
        printf("Enter your name: ");
        fgets(name, sizeof name, stdin);
    
        if((p = strchr(name, '\n'))) *p = 0;
    
        printf("Greetings, %s.\n", name);
    
        return 0;
    }
    $ gcc -g greet.c -o greet
    $ ./greet
    Enter your name: dwk
    Greetings, dwk.
    $
    Looks good. Now I'll run the program again:
    Code:
    $ ./greet
    Enter your name:
    In another terminal, I now type the following. "px aux" lists the processes which are running. grep searches for the string "greet" in this list; it finds the process and it finds itself too.The second number in ps aux's output is the process ID. The command on the right shows you which program is which.
    Code:
    $ ps aux | grep greet
    dwk 29133  0.0  0.0   1516   328 pts/5    S+   16:27   0:00 ./greet
    dwk 29135  0.0  0.0   3908   668 pts/6    S+   16:27   0:00 grep greet
    $ gdb ./greet 29133
    GNU gdb Red Hat Linux (6.5-37.el5_2.2rh)
    Copyright (C) 2006 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for details.
    This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
    
    Attaching to program: greet, process 29133
    Reading symbols from /lib/libc.so.6...done.
    Loaded symbols for /lib/libc.so.6
    Reading symbols from /lib/ld-linux.so.2...done.
    Loaded symbols for /lib/ld-linux.so.2
    0x008d8402 in __kernel_vsyscall ()
    (gdb)
    I've attached onto the process, and it is stopped for now by GDB until I type "continue" (or "c" for short). Now I can see what's happening.
    Code:
    (gdb) bt
    #0  0x008d8402 in __kernel_vsyscall ()
    #1  0x0099aa43 in __read_nocancel () from /lib/libc.so.6
    #2  0x0093ba28 in _IO_file_read_internal () from /lib/libc.so.6
    #3  0x0093cd80 in _IO_new_file_underflow () from /lib/libc.so.6
    #4  0x0093d47b in _IO_default_uflow_internal () from /lib/libc.so.6
    #5  0x0093e7ed in __uflow () from /lib/libc.so.6
    #6  0x00932176 in _IO_getline_info_internal () from /lib/libc.so.6
    #7  0x009320c1 in _IO_getline_internal () from /lib/libc.so.6
    #8  0x0093103a in fgets () from /lib/libc.so.6
    #9  0x08048443 in main () at greet.c:8
    (gdb) frame 9
    #9  0x08048443 in main () at greet.c:8
    8           fgets(name, sizeof name, stdin);
    (gdb) print name
    $1 = '\0' <repeats 5492 times>,
    "�\203*\000\000\000\000\000�%\000\000�����/+\000����@�)\
    000\000\220�\000�%\000\000\003\000\000\0002\000\000\000����", '\0'
    <repeats 45 times>,
    "\220\215\000\000`�\000\234Q�\000\234Q�\000\000\000\000\000\005\000\000\00
    0\000`�\000\000\220�\000|\211�\000���\000\000�\023\000\003\000\000
    \0001�*\000�/+\000j\002\000\000X6+\0004���e�*\000\b\000\000\000�/+
    \000/\000\000\000\000���L���e�*\000\b\000\000\000\017\000\000\000�
    ��/+\000\230���\\)*\000p����?+\000\017\000\000\000\001\000\000\000
    \000"...
    (gdb)
    Hmm, looks like name is uninitialized. Let's set a breakpoint after this line, to let fgets() finish.

    Code:
    (gdb) l
    3
    4       int main() {
    5           char name[BUFSIZ], *p;
    6
    7           printf("Enter your name: ");
    8           fgets(name, sizeof name, stdin);
    9
    10          if((p = strchr(name, '\n'))) *p = 0;
    11
    12          printf("Greetings, %s.\n", name);
    (gdb) break 12
    Breakpoint 1 at 0x8048468: file greet.c, line 12.
    (gdb) continue
    Continuing.
    Now GDB has released control of the program and it's running once again. At this point I go to the other terminal and type "dwk" into the program. Back in GDB,
    Code:
    Breakpoint 1, main () at greet.c:12
    12          printf("Greetings, %s.\n", name);
    (gdb) print name
    $2 = "dwk", '\0' <repeats 5489 times>,
    "�\203*\000\000\000\000\000�%\000\000�����/+\000����@�)\
    000\000\220�\000�%\000\000\003\000\000\0002\000\000\000����", '\0'
    <repeats 45 times>,
    "\220\215\000\000`�\000\234Q�\000\234Q�\000\000\000\000\000\005\000\000\00
    0\000`�\000\000\220�\000|\211�\000���\000\000�\023\000\003\000\000
    \0001�*\000�/+\000j\002\000\000X6+\0004���e�*\000\b\000\000\000�/+
    \000/\000\000\000\000���L���e�*\000\b\000\000\000\017\000\000\000�
    ��/+\000\230���\\)*\000p����?+\000\017\000\000\000\001\000"...
    (gdb) continue
    Continuing.
    
    Program exited normally.
    (gdb) quit
    $
    So there you have it.
    Last edited by dwks; 09-12-2009 at 04:34 PM. Reason: wrapped long GDB lines
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  14. #14
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    I did as you told, and got a backtrace in gdb of where the segfault occurs, but I cannot print out the function's locat variables!

    Here is the offending function:

    Code:
    void ll_ins_str(LINKED_LINE* ll, char* str, unsigned long col)  //Insert string
    {
    	unsigned long len = strlen(str);
    	ll_realloc(ll, ll->len+len);     //Reallocate RAM
    	memmove(ll->text+col+len, ll->text+col, (ll->len-col+1)*sizeof(char)); //FIXME segfault
    	memcpy(ll->text+col, str, len*sizeof(char));  //Paste in new string
    	ll->len += len;
    }
    A screenshot of the debugging session is in the attachment.

  15. #15
    ... kermit's Avatar
    Join Date
    Jan 2003
    Posts
    1,534
    Quote Originally Posted by MTK View Post
    I did as you told, and got a backtrace in gdb of where the segfault occurs, but I cannot print out the function's locat variables!

    Here is the offending function:

    Code:
    void ll_ins_str(LINKED_LINE* ll, char* str, unsigned long col)  //Insert string
    {
    	unsigned long len = strlen(str);
    	ll_realloc(ll, ll->len+len);     //Reallocate RAM
    	memmove(ll->text+col+len, ll->text+col, (ll->len-col+1)*sizeof(char)); //FIXME segfault
    	memcpy(ll->text+col, str, len*sizeof(char));  //Paste in new string
    	ll->len += len;
    }
    A screenshot of the debugging session is in the attachment.
    Judging from the info in the screenshot, you need to change the frame context. When you issued the backtrace command to gdb, you got a list of the stack frames; in your case there were five. If you issue the command frame, gdb will tell you which frame context you are currently in. Certainly you will not be in frame 2, for if you were, you would have been able to print the value of len. So run your program as before, issue the backtrace command. When you get the list of frames, choose the frame number corresponding to the function you want to examine, and use frame <number> to get into that context. Then you can issue the print command to examine the variable.

    A couple of other minor notes:

    1) You are using gcc/gdb - so when you compile, turn on as much debugging info as you can. There are levels to the amount of debugging info you can compile into the executable. For example, the -g option takes 4 options itself, -g0, -g1, -g2, -g3. -g0 is no debugging info (i.e. it negates using -g at all), whereas -g3 is the most amount of debugging info. Using -g by itself defaults to -g2. There is also a gdb specific option, -ggdb, which may or may not produce a little more debugging info as well (It is my understanding that it depends on the system). If you are only debugging with gdb (as opposed to dbx), you may as well use the -ggdb as opposed to the -g flag, as it is intended to work best with gdb. The -ggdb option also defaults to -ggdb2, but you can set the level to -ggdb0, -ggdb1, or -ggdb3 also. Give it a try!

    2) I find Peter's gdb Tutorial to be decent enough. If you want to get a pretty good understanding of how to use gdb, go through the lessons, and do the exercises. At the end, you should be able to handle yourself pretty well with gdb.
    Last edited by kermit; 09-13-2009 at 06:45 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Deleting Text from a Text file
    By Daved in forum C++ Programming
    Replies: 5
    Last Post: 11-04-2006, 09:47 PM
  2. struct question
    By caduardo21 in forum Windows Programming
    Replies: 5
    Last Post: 01-31-2005, 04:49 PM
  3. Appending text to an edit control
    By dit6a9 in forum Windows Programming
    Replies: 3
    Last Post: 08-13-2004, 09:52 PM
  4. Scrolling The Text
    By GaPe in forum C Programming
    Replies: 3
    Last Post: 07-14-2002, 04:33 PM
  5. Replies: 1
    Last Post: 07-13-2002, 05:45 PM