Line disciplines

Trying to write a CLI program with command history. I never realized how insanely difficult it is to implement your own line discipline. Who here has done this? What are your experiences and tips?

Attached: serveimage.png (1268x788, 37K)

Other urls found in this thread:

open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
twitter.com/NSFWRedditImage

GNU readline

>line discipline
what is that

Can’t you just put everything on a stack/linked list and call it a day?

I'm mostly having difficulty trying to get certain keystrokes to behave in the desired way. For example, when the user presses the backspace key I want it to move the cursor on the screen and also shift all the characters to the right of the cursor one space to the left in both the display and the input buffer. I'm having difficulty getting it to actually delete a character, as opposed to what it does now, which is duplicate the first character and shift everything to the right. I can't figure out why it's doing this.

>I can't figure out why it's doing this.
This is why people who tell you to just use vim are wrong, and using an IDE is really the superior option.
That said, it sounds like you're re-inventing ncurses and readline, and you aren't experienced in programming at all or you would've gotten something this simple right the first time.

So can you see what the issue is then? These are the functions I'm using for the line discipline. One modifies the input buffer and the other writes the new input buffer to the terminal after the prompt.

// TODO: Test this function
void modify_buffer( char *buf, int c, int *i ){
// Backspace key:
if( c == 0x7f && echo == ON ){
if( *i ) (*i)--;
while( buf[*i] ){
buf[(*i)++] = buf[(*i)+1];
// Pay close attention to operator precedence here.
}
}
else if( c == 0x1b ){
c = getchar();
if( c == '[' ){
c = getchar();
// Up arrow key:
if( c == 'A' ){
// TODO: Command history
}
// Down arrow key:
else if( c == 'B' ){
}
// Right arrow key:
else if( c == 'C' ){
}
// Left arrow key:
else if( c == 'D' ){
}
}
// Escape key:
else{
ungetc( c, stdin );
mode_switch();
}
}
// Any other key:
else{
buf[(*i)++] = c;
}
}

// TODO: Test this function
void write_buffer( char *buf, int *i ){
for( int j = 1; j < *i; j++ ){
putchar( '\b' );
}
printf( "\e[K" );
printf( "%s", buf );
fflush( stdout );
}


I can only guess that I'm getting the operator precedence wrong somewhere, since I did that fancy combination of pre- and post-increments, but I don't see anything logically that would be out of place.

He's hooked on cocaine.

No, the line discipline is the module that determines how the terminal screen responds to keystrokes typed into the terminal. Things like echoing characters that are typed, and erasing characters when the backspace key is typedare the job of the line discipline.

Emacs comint mode

Attached: emacs_logo_no_border.png (604x519, 43K)

obsolete garbage
you can use linenoise instead

Your style is really bad. I get it that you're trying to make it look like you were buddies with dennis ritchie in the 70s or something but that's not conductive to building something that actually works.
For instance, wtf is "i"? Do your variables really have to be undocumented single letters?

You don't use i and j for loop iterators???

>wtf is "i"?
You know what? Now that you mention it, I don't even remember what i is. I'd have to go back and look at the code again.

Sure, but there's a difference between using a simple "i" int as a loop index (not "iterator") and having it as a non obvious int pointer parameter to a function without even mentioning what it is in the comments.

Nevermind I found the solution guys.

The above code will probably make more sense if I post the input getter function that calls both those functions. i is in fact an index into the input buffer that is being passed to the functions by reference.

// TODO: Test this function
void get_input_line( char *buf, int len, FILE *fp ){
int c, i;
for( i = 0; i < len; i++ ){
buf[i] = '\0';
}
i = 0;
while( (c = getchar()) != '\n' && i < len-1 ){
modify_buffer( buf, c, &i );
if( mode == CMD_MODE || echo == ON ) write_buffer( buf, &i );
}
putchar( '\n' );
}

put spaces outside the parens of control flow statements and function calls, not in them. Change obscure var names to something readable. Use /* */ comments, instead of //. I would put braces on their own lines, but that's just a matter of preference. Also, fuck off with using the iterator like that. Just pass a size_t to the modify_buffer, and use a for loop.

but if, while for etc are functions too

/* TODO: Test this function */
void get_input_line( char *buf, int len, FILE *fp )
{
__builtin_memset(buf, '\0', len);
int c;
int i = 0;
/* Change logic from here */


You're doing a lot of weird shit.

>but if, while for etc are functions too
No, they're not; they're control flow constructs. Just because they have their own scope, doesn't mean it's a function call. Just look at the disassembly.

>__builtin_memset
I'll have to remember that.

Just use memset, I only did that to bypass 4channlel filters. Are you new to programming?

No, but I haven't read a C book since 2010. I'm not familiar with stuff that isn't covered in K&R and C in a Nutshell (C99).

>No, but I haven't read a C book since 2010
Ah okay. I consider myself an intermediate C programmer, I can see the gaps in your knowledge/styles, if that makes sense.

>I'm not familiar with stuff that isn't covered in K&R and C in a Nutshell (C99).
Neither am I; I prefer to use C11.

open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
better than a book, if you at least read one

The first part of that function can be replaced by memset, and I dunno where you get *buf from but maybe just do calloc instead.
With respect to the modify_buffer, it should work and your bug is somewhere else, but pass i just as an integer, you don't need to pass a pointer. Right now what you are doing is calling write_buffer with i pointing at the character following the null terminator for the modified buf

This

Or editline.

I've been reading and thinking about your code a little more and I figured that you said it is repeating the first character and then outputting the string, but the resulting string IS modified in the right place, right? So if you change the for in write_buffer to (int j = 0; ...) and when l done move the cursor to the right position using terminal commands, you should be good. But I still suggest passing i as the appropriate pre-calculated integer, since you aren't really getting any extra information you couldn't figure out yourself by passing it as a pointer

it's just more needless mental work. Ideally, you shouldn't have to pore through the rest of the code to understand a single function.

Just use curses, jesus fucking christ.

Strangely, I just noticed that before reading this. It seems this is another one of those "off-by-one" errors, which is why it was so hard to find.

Isn't that for full-screen programs?

You might want to invest in this book if you're doing linux programming.

Attached: linuxprogramming.png (500x660, 120K)

It's for making text user interfaces in terminals. Full screen doesn't make a difference.

Yeah, the good news is it's the same in almost every language, and you'll learn to be more careful and think clearer about those details the longer you program

I don't know about that. I've been coding since 2003, and off-by-one errors are still such a pain in the ass to detect and debug, I always just approach them through trial and error.

Well, then stop doing that and actually try to think what's the correct number lol. Unless you enjoy debugging.

This. Debugging can sometimes drive me to question my sanity.

Based.