C - 打印以控制可删除的字符串

时间:2015-10-24 09:26:37

标签: c console

我正在尝试构建一个打印"默认值的程序"用户可以通过删除部分内容并再次书写来快速更改。

该程序将打印例如:

enter app name: xcode

用户应该能够更换" xcode"字符串(但不是":"部分)然后程序应该得到整个输入。

有没有办法在纯C中使用标准控制台OSX使用? 非常感谢

1 个答案:

答案 0 :(得分:0)

这将在屏幕上显示提示和默认输入。箭头键将向右和向左移动光标。模式可以从INS(插入)更改为OVW (覆盖)使用Ins键。 Enter将终止输入,因为太多字符。可以使用LETTERS |的某些组合来限制有效字符NUMBERS | DOT |空间|签名| PUNCT。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/ioctl.h>

#define ESC          27
#define INSERT       50
#define DELETE       51
#define PGUP         53
#define PGDN         54
#define ARROWRIGHT   67
#define ARROWLEFT    68
#define END          70
#define HOME         72
#define OTHER        79
#define BRACKETLEFT  91
#define TILDE       126
#define BACKSPACE   127

#define LETTERS       1
#define NUMBERS       2
#define DOT           4
#define SPACE         8
#define SIGN         16
#define PUNCT        32

#define SIZE         30

static const int STDIN = 0;

typedef struct Field {
    int row;//place input on screen row and col
    int col;
    int contentsize;//allowed length of input
    int inputat;//input at this index
    int used;//characters used so far
    int valid;//limit input to these characters
    int insert;//mode insert/overwrite
    int promptrow;//place prompt on screen row and col
    int promptcol;
    int promptsize;
    int lastline;
    int lastcol;
    int stopon;//stop input with this character
    char *content;
    char *prompt;
} field;

int kbhit(void)
{
    int bytesWaiting;

    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
}

void prompt ( field *psfield) {
    printf ( "\033[%d;%dH", psfield->promptrow, psfield->promptcol);
    printf ( "%s", psfield->prompt);
}

int input ( field *psfield) {
    int ch = 0;
    int to = 0;

    printf ( "\033[%d;1H", psfield->lastline);
    if ( psfield->insert) {
        printf ( "INS");
    }
    else {
        printf ( "OVW");
    }

    printf ( "\033[%d;%dH", psfield->row, psfield->col);
    printf ( "\033[K");//erase to end of line
    printf ( "%s", psfield->content);
    printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);

    while ( ( ch = getchar ()) != psfield->stopon) {
        printf ( "\033[%d;10H", psfield->lastline);
        printf ( "\033[K");//erase to end of line
        printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
        if (
           ( ( psfield->valid & LETTERS && isalpha ( ch)))
        || ( ( psfield->valid & SPACE   && ch == ' '))
        || ( ( psfield->valid & DOT     && ch == '.'))
        || ( ( psfield->valid & SIGN    && ( ch == '+' || ch == '-')))
        || ( ( psfield->valid & NUMBERS && isdigit ( ch)))
        || ( ( psfield->valid & PUNCT   && ispunct ( ch)))
        ) {
            if ( psfield->insert && psfield->inputat < psfield->used && psfield->used < psfield->contentsize-3) {
                //expand
                psfield->used++;
                for ( to = psfield->used; to >= psfield->inputat; to--) {
                    psfield->content[to + 1] = psfield->content[to];
                }
                printf ( "\033[%d;%dH", psfield->row, psfield->col);
                printf ( "\033[K");//erase to end of line
                printf ( "%s", psfield->content);
            }
            printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
            printf ( "%c", ch);
            psfield->content[psfield->inputat] = ch;
            psfield->inputat++;
            if ( psfield->inputat > psfield->used) {
                psfield->used = psfield->inputat;
            }
            if ( psfield->inputat == psfield->used) {
                psfield->content[psfield->inputat] = '\0';
            }
            if ( psfield->inputat >= psfield->contentsize-1) {
                return -1;
            }
            continue;
        }

        if ( isprint ( ch)) {
            printf ( "\033[%d;10H", psfield->lastline);
            printf ( "\033[48;5;124m");//set color white text on red
            printf ( "Invalid character");
            printf ( "\033[0m");//reset to default colors
            printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
            continue;
        }
        if ( ch == BACKSPACE) {
            if ( psfield->inputat) {
                psfield->inputat--;
                //contract
                for ( to = psfield->inputat; to <= psfield->used; to++) {
                    psfield->content[to] = psfield->content[to + 1];
                }
                psfield->used--;
                printf ( "\033[%d;%dH", psfield->row, psfield->col);
                printf ( "\033[K");//erase to end of line
                printf ( "%s", psfield->content);
                printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
            }
        }
        if ( ch == ESC) {
            if ( !kbhit ( )) {
                continue;
            }
            ch = getchar ( );
            if ( ch == OTHER) {
                ch = getchar ( );
                if ( ch == HOME) {
                    psfield->inputat = 0;
                    printf ( "\033[%d;%dH", psfield->row, psfield->col);
                    ch = getchar ( );
                }
                if ( ch == END) {
                    psfield->inputat = psfield->used;
                    printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
                    ch = getchar ( );
                }
            }
            if ( ch == BRACKETLEFT) {
                ch = getchar ( );
                if ( ch == INSERT) {
                    ch = getchar ( );
                    if ( ch == TILDE) {
                        psfield->insert = !psfield->insert;
                        printf ( "\033[%d;1H", psfield->lastline);
                        if ( psfield->insert) {
                            printf ( "INS");
                        }
                        else {
                            printf ( "OVW");
                        }
                        printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
                    }
                }
                if ( ch == DELETE) {
                    ch = getchar ( );
                    if ( ch == TILDE) {
                        //contract
                        for ( to = psfield->inputat; to <= psfield->used; to++) {
                            psfield->content[to] = psfield->content[to + 1];
                        }
                        psfield->used--;
                        printf ( "\033[%d;%dH", psfield->row, psfield->col);
                        printf ( "\033[K");//erase to end of line
                        printf ( "%s", psfield->content);
                        printf ( "\033[%d;%dH", psfield->row, psfield->col + psfield->inputat);
                    }
                }
                if ( ch == ARROWRIGHT) {
                    if ( psfield->inputat < psfield->used) {
                        printf ( "\033[C");//cursor right
                        psfield->inputat++;
                    }
                }
                if ( ch == ARROWLEFT) {
                    if ( psfield->inputat) {
                        printf ( "\033[D");//cursor left
                        psfield->inputat--;
                    }
                }
            }
            else {
                ungetc ( ch, stdin);
            }
        }
    }
    return 0;
}

int main ( ) {
    struct termios oldattr, newattr;
    struct winsize w;
    field sfield = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', NULL, NULL};

    //set terminal
    tcgetattr( STDIN, &oldattr );
    newattr = oldattr;
    newattr.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN, TCSANOW, &newattr );
    setbuf(stdin, NULL);

    //get terminal dimensions
    ioctl( 0, TIOCGWINSZ, &w );
    sfield.lastline = w.ws_row;
    sfield.lastcol = w.ws_col;

    printf ( "\033[2J");//clear screen
    sfield.row = 5;
    sfield.col = 26;
    sfield.promptrow = 5;
    sfield.promptcol = 1;
    sfield.stopon = '\n';//newline will end input
    sfield.valid = LETTERS | SPACE | DOT;
    sfield.contentsize = 40;//allowed length of input
    if ( ( sfield.content = calloc ( sfield.contentsize, 1)) == NULL) {
        printf ( "calloc failed\n");
        return 1;
    }
    sfield.prompt = strdup ( "Enter letters or spaces:");
    sfield.promptsize = strlen ( sfield.prompt) + 1;

    strcpy ( sfield.content, "default");
    sfield.used = strlen ( sfield.content);


    prompt ( &sfield);
    input ( &sfield);

    printf ( "\n\ninput was [%s]\n", sfield.content);
    printf ( "\n\nbye\n");

    free ( sfield.content);
    free ( sfield.prompt);

    //restore terminal
    tcsetattr( STDIN, TCSANOW, &oldattr );

    return 0;
}