Friday, May 6, 2011

edit a body of text from the command line in C

I am writing some small program in C--some log in, log out, create account, send message to another account deal--that I can build on and change and innovate and polish up to refine my skills in C.

What I am trying to figure out how to implement is part of the "compose message" option. After you are prompted for the recipient, subject, and body of the message, I want to be able to go back and edit them--without having to re-type the whole thing. So if I chose the "edit message" option it would write out what I had and let me go back and change something.

I've done something sort of like this in x86 assembly, or started to do something like this, so I understand basically how this would be implemented at the machine level but I don't know how to implement it in C. Does anyone know how to do this? Things like:

  • a good way to handle potentially lengthly input
  • how to write out text and allow the user to edit it, without going back beyond a certain point
  • how to control the position of the cursor

are baffling me in C.

Also, if this has been done before, and there exists a library of functions for things like this (even a standard library I don't know about) please note that I do want to roll my own for the purpose of learning.

Edit

Alright so I guess the method I have in my head is to read the message body one character at a time, so that I can account for carriage returns and create a multi-line message. But I'm not sure how I would backspace through it. I guess it would be really hard to do this from the command line? If not impossible, to move the cursor back and erase characters which are already out of the input buffer... Would I have to "re-draw the screen" every time? Like can I just take control of the whole console and just read and write keystrokes to certain positions? Or is this too close to the machine? I sort of did it with assembly but that used 16 bit interrupts, which I'm not allowed to use in C... This is what I wrote in assembly:

alt text

where the program would convert a byte value to two character codes representing that byte, then jump over to the right column and write the original byte (which showed up as a character), then jump back and write out two more hex numbers, in the next slot... and so on, left to right, top to bottom... it was easy, but I have NO idea how I would implement that in C. All I can do is INT 21 style input and output, writing lines to the console which scrolls the window up and so forth.

From stackoverflow
  • I wrote a text editor.

    I've been doing C for twenty years.

    The data structure for the text was the most complex single data structure I have ever written; this being a data structure which can, as you specify, efficiently handle arbitrary length text.

    If you are new to C, you are biting off more than you can chew.

    I suggest a simple data structure, like a buffer or linked list - you can't handle arbitrary length text, but it's better than nothing.

    Carson Myers : well what I was thinking is that I could read the input one character at a time, add it to a buffer, when the buffer fills up realloc() it, etc. But what I don't know how to do is backspace through it... I'll update my question, I've thought about the process a bit
    Blank Xavier : realloc() typically will memcpy() the existing buffer into the new buffer. This is not a scalable. It's fine for a single line - but if you only have a single line, why not just malloc() 1024 bytes and be done with it.
    Carson Myers : what if I doubled the size of the buffer at each realloc()? That way it would grow exponentially... besides I'm finding that the biggest issue is being able to edit text in the buffer
    Blank Xavier : With a flat buffer the straightfoward approach, when a character is added or removed, is to memcpy() the buffer to the right of the insert/delete point up or down by one. This as you can imagine sucks :-) but it's simple and is tolerable for a small buffer.
  • That is highly related to the system (OS). I think you are trying to do this on Windows.

    You can use the Windows Console API to do it.

    If you want to study some libraries for reference (before you roll your own), a good library is the GNU Readline.

    Blank Xavier : The guy specified he wants to roll his own.
    Francis : The Windows Console API is for him to roll his own. The Readline is good for studying.
    Carson Myers : alright, I'll look at that API and see how to use it, and read that library to see how I might implement it
  • You say you want to avoid using libraries (standard or otherwise), but unfortunately in C all input & output is performed via libraries - the language itself has absolutely no input/output facilities.

    So you are doomed to use libraries of some sort. Given that you seem to want a textual interface, I suggest vtaking a look at the portable version of curses at http://pdcurses.sourceforge.net.

    Carson Myers : alright I shall take a look at that
    Chris Lutz : Is PDCurses any more portable than ncurses?
    anon : I've only used it on Windows, and I haven't used ncurses, so I can't really comment.
  • There is no way in ANSI C to make a portable line-editor. If you roll your own, you will have to reroll it for every new operating system you want your program to work on.

    If I may make a suggestion, I would use a pre-existing library to do all of that hard, platform-specific dirty work, and with that leg-up, learn how to handle things like arbitrary-length input and such. Then, when your code works (and is good), learn how to do all that dirty work, and take away the library-crutch. That way, you're not tackling the whole thing - you're breaking it down into more manageable parts.

    Even this is a bit of an oversimplification. It took me quite a while to learn how to handle arbitrary-length input.

    Also, know that, if you want your code to be portable, removing the library dependency will mean that, if you want to port it, you'll have to either a) rewrite all that dirty-work code, or b) add the library back in.

    To end this all on a joke, this is your brain with libraries:

    Pigmaei gigantum humeris impositi plusquam ipsi gigantes vident.
    (If I have seen a little further it is by standing on the shoulders of Giants.)
    --Isaac Newton

    This is your brain without libraries:

    If I have not seen as far as others, it is because giants were standing on my shoulders.
    --Hal Abelson

  • As for holding the data, I'm guessing that a rope'd be the best data structure available: In a simplified form, it's a tree of strings. When you want to print it on-screen, all you should need to do is walk it in pre-order, and print it. Splitting a line in two involves a relatively simple tree op: add a leaf. The hard part would be in splitting the string itself (copy the string, set the old end to 0, add the leaf, add the pointer)...

    Now, there's the issue of tracking the cursor... you could leave a breadcrumb trail to follow from the root to where the cursor currently is.

    There's also thinking about a resizable console... that is, if you want your editor to work whether the terminal is 80 or 200 chars wide...

    Blank Xavier : What happens when you load in a file which is a single line say 10mb in length? do you have a rope with one element which contains a single 10mb string? if so, what happens when you delete the first character from that string? a 10mb memcpy?
    Tordek : I have never actually used a rope; but I'm guessing that you can, first, determine a max length for a leaf string (usually BUFSIZ, which is a good number for anything disk-related), and read a chunk at a time.

0 comments:

Post a Comment