Annotation of libaitcli/src/aitcli.c, revision 1.4

1.1       misho       1: /*************************************************************************
                      2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
                      3: *  by Michael Pounov <misho@openbsd-bg.org>
                      4: *
                      5: * $Author: misho $
1.4     ! misho       6: * $Id: aitcli.c,v 1.3.2.3 2011/05/29 22:48:23 misho Exp $
1.1       misho       7: *
1.4     ! misho       8: **************************************************************************
        !             9: The ELWIX and AITNET software is distributed under the following
        !            10: terms:
        !            11: 
        !            12: All of the documentation and software included in the ELWIX and AITNET
        !            13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
        !            14: 
        !            15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
        !            16:        by Michael Pounov <misho@elwix.org>.  All rights reserved.
        !            17: 
        !            18: Redistribution and use in source and binary forms, with or without
        !            19: modification, are permitted provided that the following conditions
        !            20: are met:
        !            21: 1. Redistributions of source code must retain the above copyright
        !            22:    notice, this list of conditions and the following disclaimer.
        !            23: 2. Redistributions in binary form must reproduce the above copyright
        !            24:    notice, this list of conditions and the following disclaimer in the
        !            25:    documentation and/or other materials provided with the distribution.
        !            26: 3. All advertising materials mentioning features or use of this software
        !            27:    must display the following acknowledgement:
        !            28: This product includes software developed by Michael Pounov <misho@elwix.org>
        !            29: ELWIX - Embedded LightWeight unIX and its contributors.
        !            30: 4. Neither the name of AITNET nor the names of its contributors
        !            31:    may be used to endorse or promote products derived from this software
        !            32:    without specific prior written permission.
        !            33: 
        !            34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
        !            35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            37: ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            44: SUCH DAMAGE.
        !            45: */
1.1       misho      46: #include "global.h"
1.3       misho      47: #include "cli.h"
1.1       misho      48: 
                     49: 
                     50: #pragma GCC visibility push(hidden)
                     51: 
                     52: // ------------------------------------------------
                     53: 
                     54: int cli_Errno;
                     55: char cli_Error[STRSIZ];
                     56: 
1.3       misho      57: #pragma GCC visibility pop
                     58: 
                     59: // cli_GetErrno() Get error code of last operation
                     60: inline int
                     61: cli_GetErrno()
                     62: {
                     63:        return cli_Errno;
                     64: }
                     65: 
                     66: // io_GetError() Get error text of last operation
                     67: inline const char *
                     68: cli_GetError()
                     69: {
                     70:        return cli_Error;
                     71: }
                     72: 
                     73: // cli_SetErr() Set error to variables for internal use!!!
                     74: inline void
                     75: cli_SetErr(int eno, char *estr, ...)
                     76: {
                     77:        va_list lst;
                     78: 
                     79:        cli_Errno = eno;
                     80:        memset(cli_Error, 0, STRSIZ);
                     81:        va_start(lst, estr);
                     82:        vsnprintf(cli_Error, STRSIZ, estr, lst);
                     83:        va_end(lst);
                     84: }
1.2       misho      85: 
1.3       misho      86: // ------------------------------------------------------------
                     87: 
                     88: static inline void
                     89: clrscrEOL(linebuffer_t * __restrict buf)
                     90: {
                     91:        register int i;
                     92: 
                     93:        if (buf) {
                     94:                write(buf->line_out, K_CR, 1);
1.1       misho      95: 
1.3       misho      96:                for (i = 0; i < buf->line_len; i++)
                     97:                        write(buf->line_out, K_SPACE, 1);
                     98:        }
                     99: }
1.1       misho     100: 
1.3       misho     101: static inline void
                    102: printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
1.2       misho     103: {
1.3       misho     104:        if (buf) {
                    105:                write(buf->line_out, K_CR, 1);
                    106: 
                    107:                if (prompt && buf->line_prompt)
                    108:                        write(buf->line_out, buf->line_prompt, buf->line_bol);
                    109: 
                    110:                write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len);
                    111:        }
1.2       misho     112: }
                    113: 
1.3       misho     114: static inline void
                    115: printfCR(linebuffer_t * __restrict buf, int prompt)
1.2       misho     116: {
1.3       misho     117:        if (buf) {
                    118:                write(buf->line_out, K_CR, 1);
1.2       misho     119: 
1.3       misho     120:                if (prompt)
                    121:                        if (prompt && buf->line_prompt)
                    122:                                write(buf->line_out, buf->line_prompt, buf->line_bol);
1.2       misho     123:        }
1.3       misho     124: }
                    125: 
                    126: static inline void
                    127: printfNL(linebuffer_t * __restrict buf, int prompt)
                    128: {
                    129:        if (buf) {
                    130:                write(buf->line_out, K_ENTER, 1);
                    131: 
                    132:                if (prompt)
                    133:                        if (prompt && buf->line_prompt)
                    134:                                write(buf->line_out, buf->line_prompt, buf->line_bol);
                    135:        }
                    136: }
                    137: 
                    138: // ------------------------------------------------------------
                    139: 
                    140: static int
                    141: bufCHAR(int idx, void * __restrict buffer)
                    142: {
                    143:        linebuffer_t *buf = buffer;
                    144:        int pos;
                    145: 
                    146:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    147:                return RETCODE_ERR;
                    148: 
                    149:        pos = buf->line_eol - buf->line_bol;
                    150: 
                    151:        if (buf->line_mode == LINEMODE_INS)
                    152:                memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos, 
                    153:                                buf->line_len - buf->line_eol);
                    154:        if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1)
                    155:                buf->line_len += buf->line_keys[idx].key_len;
                    156:        buf->line_eol += buf->line_keys[idx].key_len;
                    157: 
                    158:        memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
                    159:        buf->line_buf[buf->line_len - 1] = 0;
                    160: 
                    161:        write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
                    162: 
                    163:        if (buf->line_mode == LINEMODE_INS) {
                    164:                write(buf->line_out, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len, 
                    165:                                buf->line_len - buf->line_eol);
                    166:                printfEOL(buf, -1, 1);
1.2       misho     167:        }
1.3       misho     168:        return RETCODE_OK;
                    169: }
                    170: 
                    171: static int
                    172: bufEOL(int idx, void * __restrict buffer)
                    173: {
                    174:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    175:                return RETCODE_ERR;
                    176: 
                    177:        printfCR(buffer, 1);
                    178:        return RETCODE_EOL;
                    179: }
                    180: 
                    181: static int
                    182: bufEOF(int idx, void * __restrict buffer)
                    183: {
                    184:        /*
                    185:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    186:                return RETCODE_ERR;
                    187:        */
                    188: 
                    189:        return RETCODE_EOF;
                    190: }
                    191: 
                    192: static int
                    193: bufUP(int idx, void * __restrict buffer)
                    194: {
                    195:        linebuffer_t *buf = buffer;
                    196:        int pos;
                    197: 
                    198:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    199:                return RETCODE_ERR;
                    200: 
                    201:        if (!buf->line_h)
                    202:                buf->line_h = TAILQ_FIRST(&buf->line_history);
                    203:        else
                    204:                buf->line_h = TAILQ_NEXT(buf->line_h, hist_next);
                    205:        if (!buf->line_h)
                    206:                return RETCODE_OK;
                    207: 
                    208:        clrscrEOL(buf);
                    209:        cli_freeLine(buf);
                    210: 
                    211:        pos = buf->line_eol - buf->line_bol;
                    212: 
                    213:        buf->line_len += buf->line_h->hist_len;
                    214:        buf->line_eol += buf->line_h->hist_len;
                    215: 
                    216:        memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
                    217:        buf->line_buf[buf->line_len - 1] = 0;
                    218: 
                    219:        printfEOL(buf, -1, 1);
                    220:        return RETCODE_OK;
                    221: }
                    222: 
                    223: static int
                    224: bufDOWN(int idx, void * __restrict buffer)
                    225: {
                    226:        linebuffer_t *buf = buffer;
                    227:        int pos;
                    228: 
                    229:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    230:                return RETCODE_ERR;
                    231: 
                    232:        if (!buf->line_h)
                    233:                buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead);
                    234:        else
                    235:                buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next);
                    236:        if (!buf->line_h)
                    237:                return RETCODE_OK;
                    238: 
                    239:        clrscrEOL(buf);
                    240:        cli_freeLine(buf);
                    241: 
                    242:        pos = buf->line_eol - buf->line_bol;
                    243: 
                    244:        buf->line_len += buf->line_h->hist_len;
                    245:        buf->line_eol += buf->line_h->hist_len;
                    246: 
                    247:        memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
                    248:        buf->line_buf[buf->line_len - 1] = 0;
                    249: 
                    250:        printfEOL(buf, -1, 1);
                    251:        return RETCODE_OK;
                    252: }
                    253: 
                    254: static int
                    255: bufCLR(int idx, void * __restrict buffer)
                    256: {
                    257:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    258:                return RETCODE_ERR;
                    259: 
                    260:        clrscrEOL(buffer);
                    261:        cli_freeLine(buffer);
                    262: 
                    263:        printfCR(buffer, 1);
                    264:        return RETCODE_OK;
                    265: }
                    266: 
                    267: static int
                    268: bufBS(int idx, void * __restrict buffer)
                    269: {
                    270:        linebuffer_t *buf = buffer;
                    271: 
                    272:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    273:                return RETCODE_ERR;
                    274: 
                    275:        if (buf->line_bol < buf->line_eol) {
                    276:                clrscrEOL(buf);
                    277: 
                    278:                buf->line_eol--;
                    279:                buf->line_len--;
                    280:                memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
                    281:                                buf->line_buf + buf->line_eol - buf->line_bol + 1, 
                    282:                                buf->line_len - buf->line_eol);
                    283:                buf->line_buf[buf->line_len - 1] = 0;
                    284: 
                    285:                printfEOL(buf, buf->line_len - 1, 1);
                    286:                printfEOL(buf, -1, 1);
                    287:        }
                    288: 
                    289:        return RETCODE_OK;
                    290: }
                    291: 
                    292: static int
                    293: bufBTAB(int idx, void * __restrict buffer)
                    294: {
                    295:        linebuffer_t *buf = buffer;
                    296: 
                    297:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    298:                return RETCODE_ERR;
                    299: 
                    300:        if (buf->line_bol < buf->line_eol) {
                    301:                clrscrEOL(buf);
                    302: 
                    303:                buf->line_len = buf->line_eol - buf->line_bol + 1;
                    304:                buf->line_buf[buf->line_len - 1] = 0;
                    305: 
                    306:                printfEOL(buf, -1, 1);
1.2       misho     307:        }
                    308: 
1.3       misho     309:        return RETCODE_OK;
                    310: }
                    311: 
                    312: static int
                    313: bufMODE(int idx, void * __restrict buffer)
                    314: {
                    315:        linebuffer_t *buf = buffer;
                    316: 
                    317:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    318:                return RETCODE_ERR;
                    319: 
                    320:        buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS;
                    321:        return RETCODE_OK;
                    322: }
                    323: 
                    324: static int
                    325: bufBEGIN(int idx, void * __restrict buffer)
                    326: {
                    327:        linebuffer_t *buf = buffer;
                    328: 
                    329:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    330:                return RETCODE_ERR;
                    331: 
                    332:        buf->line_eol = buf->line_bol;
                    333: 
                    334:        printfCR(buf, 1);
                    335:        return RETCODE_OK;
                    336: }
                    337: 
                    338: static int
                    339: bufEND(int idx, void * __restrict buffer)
                    340: {
                    341:        linebuffer_t *buf = buffer;
                    342: 
                    343:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    344:                return RETCODE_ERR;
                    345: 
                    346:        buf->line_eol = buf->line_len - 1;
                    347: 
                    348:        printfEOL(buf, -1, 1);
                    349:        return RETCODE_OK;
1.2       misho     350: }
                    351: 
1.3       misho     352: static int
                    353: bufLEFT(int idx, void * __restrict buffer)
1.2       misho     354: {
1.3       misho     355:        linebuffer_t *buf = buffer;
                    356: 
                    357:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    358:                return RETCODE_ERR;
                    359: 
                    360:        if (buf->line_bol < buf->line_eol)
                    361:                printfEOL(buf, --buf->line_eol - buf->line_bol, 1);
                    362: 
                    363:        return RETCODE_OK;
                    364: }
1.2       misho     365: 
1.3       misho     366: static int
                    367: bufRIGHT(int idx, void * __restrict buffer)
                    368: {
                    369:        linebuffer_t *buf = buffer;
                    370: 
                    371:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    372:                return RETCODE_ERR;
                    373: 
                    374:        if (buf->line_eol < buf->line_len - 1)
                    375:                printfEOL(buf, ++buf->line_eol - buf->line_bol, 1);
                    376: 
                    377:        return RETCODE_OK;
                    378: }
                    379: 
                    380: static int
                    381: bufDEL(int idx, void * __restrict buffer)
                    382: {
                    383:        linebuffer_t *buf = buffer;
                    384: 
                    385:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    386:                return RETCODE_ERR;
                    387: 
                    388:        clrscrEOL(buf);
                    389: 
                    390:        buf->line_len--;
                    391:        memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
                    392:                        buf->line_buf + buf->line_eol - buf->line_bol + 1, 
                    393:                        buf->line_len - buf->line_eol);
                    394:        buf->line_buf[buf->line_len - 1] = 0;
                    395: 
                    396:        printfEOL(buf, buf->line_len - 1, 1);
                    397:        printfEOL(buf, -1, 1);
                    398: 
                    399:        return RETCODE_OK;
                    400: }
                    401: 
                    402: static int
                    403: bufComp(int idx, void * __restrict buffer)
                    404: {
                    405:        linebuffer_t *buf = buffer;
                    406:        char *str, *s, **app, *items[MAX_PROMPT_ITEMS], szLine[STRSIZ];
                    407:        register int i, j;
                    408:        struct tagCommand *cmd, *c;
                    409:        int pos, ret = RETCODE_OK;
                    410: 
                    411:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    412:                return RETCODE_ERR;
                    413: 
                    414:        str = strdup(buf->line_buf);
                    415:        if (!str)
                    416:                return RETCODE_ERR;
1.2       misho     417:        else {
1.3       misho     418:                s = str;
                    419:                io_TrimStr((u_char*) s);
                    420:        }
                    421: 
                    422:        i = j = 0;
                    423:        c = NULL;
                    424:        memset(szLine, 0, STRSIZ);
                    425:        if (*s) {
                    426:                memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                    427:                for (app = items, i = 0; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
                    428:                                *app ? i++ : i, *app ? app++ : app);
                    429: 
                    430:                if (i) {
                    431:                        SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
                    432:                                if (cmd->cmd_level == buf->line_level && 
                    433:                                                !strncmp(cmd->cmd_name, items[0], strlen(items[0]))) {
                    434:                                        if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) {
                    435:                                                j++;
                    436:                                                c = cmd;
                    437:                                                strlcat(szLine, " ", STRSIZ);
                    438:                                                strlcat(szLine, cmd->cmd_name, STRSIZ);
                    439:                                        }
                    440:                                }
                    441:                        }
1.2       misho     442: 
1.3       misho     443:                        if (i > 1 && c) {
                    444:                                /* we are on argument of command and has complition info */
                    445:                                j++;    // always must be j > 1 ;) for arguments
                    446:                                strlcpy(szLine, c->cmd_info, STRSIZ);
                    447:                        }
                    448:                } else {
                    449:                        /* we have valid char but i == 0, this case is illegal */
                    450:                        ret = RETCODE_ERR;
                    451:                        goto endcomp;
1.2       misho     452:                }
1.3       misho     453:        } else {
                    454:                /* we on 0 position of prompt, show commands for this level */
                    455:                SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
                    456:                        if (cmd->cmd_level == buf->line_level)
                    457:                                if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) {
                    458:                                        j++;
                    459:                                        c = cmd;
                    460:                                        strlcat(szLine, " ", STRSIZ);
                    461:                                        strlcat(szLine, cmd->cmd_name, STRSIZ);
                    462:                                }
                    463:                }
                    464:        }
1.2       misho     465: 
1.3       misho     466:        /* completion show actions ... */
                    467:        if (j > 1 && c) {
                    468:                printfNL(buf, 0);
                    469:                write(buf->line_out, szLine, strlen(szLine));
                    470:                printfNL(buf, 1);
                    471:                printfEOL(buf, buf->line_len - 1, 1);
                    472:                printfEOL(buf, -1, 1);
1.2       misho     473:        }
1.3       misho     474:        if (j == 1 && c) {
                    475:                clrscrEOL(buf);
                    476:                cli_freeLine(buf);
                    477: 
                    478:                pos = buf->line_eol - buf->line_bol;
                    479: 
                    480:                buf->line_len += c->cmd_len + 1;
                    481:                buf->line_eol += c->cmd_len + 1;
                    482: 
                    483:                memcpy(buf->line_buf + pos, c->cmd_name, c->cmd_len);
                    484:                buf->line_buf[pos + c->cmd_len] = (u_char) *K_SPACE;
                    485:                buf->line_buf[buf->line_len - 1] = 0;
1.2       misho     486: 
1.3       misho     487:                printfEOL(buf, -1, 1);
1.2       misho     488:        }
                    489: 
1.3       misho     490: endcomp:
                    491:        free(str);
                    492:        return ret;
                    493: }
                    494: 
                    495: static int
                    496: bufHelp(int idx, void * __restrict buffer)
                    497: {
                    498:        linebuffer_t *buf = buffer;
                    499: 
                    500:        if (!buffer || idx < 0 || idx > MAX_BINDKEY)
                    501:                return RETCODE_ERR;
                    502: 
                    503:        cli_Cmd_Help(buf, -1, NULL);
                    504: 
                    505:        printfEOL(buf, buf->line_len - 1, 1);
                    506:        printfEOL(buf, -1, 1);
                    507:        return RETCODE_OK;
                    508: }
                    509: 
                    510: // ---------------------------------------------------------------
                    511: 
                    512: /*
                    513:  * cli_Printf() Send message to CLI session
                    514:  * @buffer = CLI buffer
                    515:  * @fmt = printf format string
                    516:  * @... = arguments defined in fmt
                    517:  * return: none
                    518: */
                    519: inline void
                    520: cli_Printf(linebuffer_t * __restrict buffer, char *fmt, ...)
                    521: {
                    522:        va_list lst;
                    523:        FILE *f;
                    524: 
                    525:        if (fmt) {
                    526:                f = fdopen(buffer->line_out, "a");
                    527:                if (!f) {
                    528:                        LOGERR;
                    529:                        return;
                    530:                }
                    531: 
                    532:                va_start(lst, fmt);
                    533:                vfprintf(f, fmt, lst);
                    534:                va_end(lst);
                    535:        } else
                    536:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    537: }
                    538: 
                    539: /*
                    540:  * cli_PrintHelp() Print help screen
                    541:  * @buffer = CLI buffer
                    542:  * return: none
                    543: */
                    544: inline void
                    545: cli_PrintHelp(linebuffer_t * __restrict buffer)
                    546: {
                    547:        if (buffer) {
                    548:                bufHelp(0, buffer);
                    549:                clrscrEOL(buffer);
                    550:        } else
                    551:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    552: }
                    553: 
                    554: 
                    555: /*
                    556:  * cli_BindKey() Bind function to key
                    557:  * @key = key structure
                    558:  * @buffer = CLI buffer
                    559:  * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
                    560: */
                    561: int
                    562: cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict buffer)
                    563: {
                    564:        register int i;
                    565: 
                    566:        if (!key || !buffer) {
                    567:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    568:                return RETCODE_ERR;
1.2       misho     569:        }
1.3       misho     570: 
                    571:        for (i = 0; i < MAX_BINDKEY; i++)
                    572:                if (key->key_len == buffer->line_keys[i].key_len && 
                    573:                                !memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) {
                    574:                        buffer->line_keys[i].key_func = key->key_func;
                    575:                        return i;
                    576:                }
                    577: 
                    578:        return RETCODE_OK;
1.2       misho     579: }
                    580: 
                    581: 
1.3       misho     582: /*
                    583:  * cli_addCommand() Add command to CLI session
                    584:  * @buffer = CLI buffer
                    585:  * @csCmd = Command name
                    586:  * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
                    587:  * @funcCmd = Callback function when user call command
                    588:  * @csInfo = Inline information for command
                    589:  * @csHelp = Help line when call help
                    590:  * return: RETCODE_ERR error, RETCODE_OK ok
                    591: */
                    592: int
                    593: cli_addCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd, 
                    594:                const char *csInfo, const char *csHelp)
1.1       misho     595: {
1.3       misho     596:        struct tagCommand *cmd;
                    597: 
                    598:        if (!buffer || !csCmd) {
                    599:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    600:                return RETCODE_ERR;
                    601:        }
                    602: 
                    603:        cmd = malloc(sizeof(struct tagCommand));
                    604:        if (!cmd) {
                    605:                LOGERR;
                    606:                return RETCODE_ERR;
                    607:        } else
                    608:                memset(cmd, 0, sizeof(struct tagCommand));
                    609: 
                    610:        cmd->cmd_level = cliLevel;
                    611:        cmd->cmd_func = funcCmd;
                    612:        cmd->cmd_len = strlcpy(cmd->cmd_name, csCmd, STRSIZ);
                    613:        if (csInfo)
                    614:                strlcpy(cmd->cmd_info, csInfo, STRSIZ);
                    615:        if (csHelp)
                    616:                strlcpy(cmd->cmd_help, csHelp, STRSIZ);
                    617:        SLIST_INSERT_HEAD(&buffer->line_cmds, cmd, cmd_next);
                    618:        return RETCODE_OK;
1.1       misho     619: }
                    620: 
1.3       misho     621: /*
                    622:  * cli_delCommand() Delete command from CLI session
                    623:  * @buffer = CLI buffer
                    624:  * @csCmd = Command name
                    625:  * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
                    626:  * return: RETCODE_ERR error, RETCODE_OK ok
                    627: */
                    628: int
                    629: cli_delCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel)
1.1       misho     630: {
1.3       misho     631:        struct tagCommand *cmd;
                    632:        int ret = RETCODE_OK;
                    633: 
                    634:        if (!buffer || !csCmd) {
                    635:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    636:                return RETCODE_ERR;
                    637:        }
                    638: 
                    639:        SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) 
                    640:                if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
                    641:                        ret = 1;
                    642:                        SLIST_REMOVE(&buffer->line_cmds, cmd, tagCommand, cmd_next);
                    643:                        free(cmd);
                    644:                        break;
                    645:                }
                    646: 
                    647:        return ret;
1.1       misho     648: }
                    649: 
1.3       misho     650: /*
                    651:  * cli_updCommand() Update command in CLI session
                    652:  * @buffer = CLI buffer
                    653:  * @csCmd = Command name
                    654:  * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
                    655:  * @funcCmd = Callback function when user call command
                    656:  * @csInfo = Inline information for command
                    657:  * @csHelp = Help line when call help
                    658:  * return: RETCODE_ERR error, RETCODE_OK ok
                    659: */
                    660: int
                    661: cli_updCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd, 
                    662:                const char *csInfo, const char *csHelp)
1.1       misho     663: {
1.3       misho     664:        struct tagCommand *cmd;
                    665:        int ret = RETCODE_OK;
                    666: 
                    667:        if (!buffer || !csCmd) {
                    668:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    669:                return RETCODE_ERR;
                    670:        }
                    671: 
                    672:        SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) 
                    673:                if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
                    674:                        ret = 1;
                    675: 
                    676:                        if (funcCmd)
                    677:                                cmd->cmd_func = funcCmd;
                    678:                        if (csInfo)
                    679:                                strlcpy(cmd->cmd_info, csInfo, STRSIZ);
                    680:                        if (csHelp)
                    681:                                strlcpy(cmd->cmd_help, csHelp, STRSIZ);
                    682: 
                    683:                        break;
                    684:                }
1.1       misho     685: 
1.3       misho     686:        return ret;
1.1       misho     687: }
                    688: 
                    689: 
                    690: /*
1.3       misho     691:  * cli_addHistory() Add line to history
                    692:  * @buffer = CLI buffer
                    693:  * @str = Add custom text or if NULL use readed line from CLI buffer
                    694:  * return: RETCODE_ERR error, RETCODE_OK ok
1.1       misho     695: */
1.3       misho     696: int
                    697: cli_addHistory(linebuffer_t * __restrict buffer, const char * __restrict str)
1.1       misho     698: {
1.3       misho     699:        struct tagHistory *h;
                    700: 
                    701:        if (!buffer) {
                    702:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    703:                return RETCODE_ERR;
                    704:        }
                    705: 
                    706:        if (!(h = malloc(sizeof(struct tagHistory)))) {
                    707:                LOGERR;
                    708:                return RETCODE_ERR;
                    709:        } else
                    710:                memset(h, 0, sizeof(struct tagHistory));
                    711: 
                    712:        if (str) {
                    713:                if (!*str) {
                    714:                        free(h);
                    715:                        return RETCODE_OK;
                    716:                }
                    717: 
                    718:                h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
                    719:        } else {
                    720:                if (!*buffer->line_buf || buffer->line_len < 2) {
                    721:                        free(h);
                    722:                        return RETCODE_OK;
                    723:                }
                    724: 
                    725:                memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len));
                    726:                io_TrimStr((u_char*) h->hist_line);
                    727:                h->hist_len = strlen(h->hist_line);
                    728:        }
1.1       misho     729: 
1.3       misho     730:        TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next);
                    731:        return h->hist_len;
                    732: }
1.1       misho     733: 
1.3       misho     734: /*
                    735:  * cli_saveHistory() Save history to file
                    736:  * @buffer = CLI buffer
                    737:  * @histfile = History filename, if NULL will be use default name
                    738:  * @lines = Maximum history lines to save
                    739:  * return: RETCODE_ERR error, RETCODE_OK ok
                    740: */
                    741: int
                    742: cli_saveHistory(linebuffer_t * __restrict buffer, const char *histfile, int lines)
                    743: {
                    744:        FILE *f;
                    745:        mode_t mode;
                    746:        char szFName[MAXPATHLEN];
                    747:        struct tagHistory *h;
                    748: 
                    749:        if (!buffer) {
                    750:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    751:                return RETCODE_ERR;
                    752:        }
                    753:        if (!histfile)
                    754:                strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
                    755:        else
                    756:                strlcpy(szFName, histfile, MAXPATHLEN);
                    757: 
                    758:        mode = umask(0177);
                    759:        f = fopen(szFName, "w");
                    760:        if (!f) {
1.1       misho     761:                LOGERR;
1.3       misho     762:                return RETCODE_ERR;
                    763:        }
                    764: 
                    765:        TAILQ_FOREACH(h, &buffer->line_history, hist_next) {
                    766:                fprintf(f, "%s\n", h->hist_line);
                    767: 
                    768:                if (lines)
                    769:                        lines--;
                    770:                else
                    771:                        break;
                    772:        }
1.1       misho     773: 
1.3       misho     774:        fclose(f);
                    775:        umask(mode);
                    776: 
                    777:        return RETCODE_OK;
1.1       misho     778: }
                    779: 
1.3       misho     780: /*
                    781:  * cli_loadHistory() Load history from file
                    782:  * @buffer = CLI buffer
                    783:  * @histfile = History filename, if NULL will be use default name
                    784:  * return: RETCODE_ERR error, RETCODE_OK ok
                    785: */
                    786: int
                    787: cli_loadHistory(linebuffer_t * __restrict buffer, const char *histfile)
                    788: {
                    789:        FILE *f;
                    790:        char szFName[MAXPATHLEN], buf[BUFSIZ];
                    791:        struct tagHistory *h;
                    792: 
                    793:        if (!buffer) {
                    794:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    795:                return RETCODE_ERR;
                    796:        }
                    797:        if (!histfile)
                    798:                strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
                    799:        else
                    800:                strlcpy(szFName, histfile, MAXPATHLEN);
                    801: 
                    802:        f = fopen(szFName, "r");
                    803:        if (!f)
                    804:                return RETCODE_OK;
                    805: 
                    806:        while (fgets(buf, BUFSIZ, f)) {
                    807:                if (!*buf || *buf == '#')
                    808:                        continue;
                    809:                else
                    810:                        io_TrimStr((u_char*) buf);
                    811: 
                    812:                if (!(h = malloc(sizeof(struct tagHistory)))) {
                    813:                        LOGERR;
                    814:                        fclose(f);
                    815:                        return RETCODE_ERR;
                    816:                } else
                    817:                        memset(h, 0, sizeof(struct tagHistory));
                    818: 
                    819:                h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ);
                    820:                TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next);
                    821:        }
                    822: 
                    823:        fclose(f);
                    824: 
                    825:        return RETCODE_OK;
                    826: }
1.1       misho     827: 
                    828: /*
1.3       misho     829:  * cli_resetHistory() Reset history search in CLI session
                    830:  * @buffer = CLI buffer
1.1       misho     831:  * return: none
                    832: */
1.3       misho     833: inline void
                    834: cli_resetHistory(linebuffer_t * __restrict buffer)
1.1       misho     835: {
1.3       misho     836:        buffer->line_h = NULL;
1.1       misho     837: }
                    838: 
1.3       misho     839: 
1.1       misho     840: /*
1.3       misho     841:  * cli_freeLine() Clear entire line
                    842:  * @buffer = CLI buffer
                    843:  * return: RETCODE_ERR error, RETCODE_OK ok
1.2       misho     844: */
1.3       misho     845: inline int
                    846: cli_freeLine(linebuffer_t * __restrict buffer)
1.2       misho     847: {
1.3       misho     848:        int code = RETCODE_ERR;
1.2       misho     849: 
1.3       misho     850:        if (buffer) {
                    851:                if (buffer->line_buf)
                    852:                        free(buffer->line_buf);
                    853: 
                    854:                buffer->line_buf = malloc(BUFSIZ);
                    855:                if (buffer->line_buf) {
                    856:                        memset(buffer->line_buf, 0, BUFSIZ);
                    857:                        buffer->line_eol = buffer->line_bol;
                    858:                        buffer->line_len = 1 + buffer->line_eol;
1.2       misho     859: 
1.3       misho     860:                        code = RETCODE_OK;
                    861:                } else
                    862:                        LOGERR;
                    863:        } else
                    864:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
1.2       misho     865: 
1.3       misho     866:        return code;
1.2       misho     867: }
                    868: 
                    869: /*
1.3       misho     870:  * cli_setPrompt() Set new prompt for CLI session
                    871:  * @buffer = CLI buffer
                    872:  * @prompt = new text for prompt or if NULL disable prompt
                    873:  * return: none
1.2       misho     874: */
1.3       misho     875: inline void
                    876: cli_setPrompt(linebuffer_t * __restrict buffer, const char *prompt)
1.2       misho     877: {
1.3       misho     878:        if (buffer) {
                    879:                if (buffer->line_prompt) {
                    880:                        free(buffer->line_prompt);
                    881:                        buffer->line_prompt = NULL;
                    882:                        buffer->line_bol = 0;
                    883:                }
                    884: 
                    885:                if (prompt) {
                    886:                        buffer->line_prompt = strdup(prompt);
                    887:                        if (buffer->line_prompt) {
                    888:                                buffer->line_bol = strlen(buffer->line_prompt);
                    889:                                buffer->line_eol = buffer->line_bol;
                    890:                                buffer->line_len = 1 + buffer->line_eol;
                    891:                        } else
                    892:                                LOGERR;
                    893:                }
                    894:        } else
                    895:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
1.2       misho     896: }
                    897: 
1.3       misho     898: 
1.2       misho     899: /*
1.3       misho     900:  * cliEnd() Clear data, Free resources and close CLI session
                    901:  * @buffer = CLI buffer
                    902:  * return: RETCODE_ERR error, RETCODE_OK ok
1.2       misho     903: */
1.3       misho     904: void
                    905: cliEnd(linebuffer_t * __restrict buffer)
1.2       misho     906: {
1.3       misho     907:        struct tagHistory *h;
                    908:        struct tagCommand *c;
                    909: 
                    910:        if (buffer) {
                    911:                while ((c = SLIST_FIRST(&buffer->line_cmds))) {
                    912:                        SLIST_REMOVE_HEAD(&buffer->line_cmds, cmd_next);
                    913:                        free(c);
                    914:                }
                    915:                while ((h = TAILQ_FIRST(&buffer->line_history))) {
                    916:                        TAILQ_REMOVE(&buffer->line_history, h, hist_next);
                    917:                        free(h);
                    918:                }
                    919: 
                    920:                if (buffer->line_prompt)
                    921:                        free(buffer->line_prompt);
1.2       misho     922: 
1.3       misho     923:                if (buffer->line_keys)
                    924:                        free(buffer->line_keys);
                    925:                if (buffer->line_buf)
                    926:                        free(buffer->line_buf);
1.2       misho     927: 
1.3       misho     928:                free(buffer);
                    929:                buffer = NULL;
                    930:        } else
                    931:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                    932: }
                    933: 
                    934: /*
                    935:  * cliInit() Start CLI session, allocate memory for resources and bind keys
                    936:  * @fin = Input device handle
                    937:  * @fout = Output device handle
                    938:  * @prompt = text for prompt, if NULL disable prompt
                    939:  * return: NULL if error or !=NULL CLI buffer
                    940: */
                    941: linebuffer_t *
                    942: cliInit(int fin, int fout, const char *prompt)
                    943: {
                    944:        linebuffer_t *buffer;
                    945:        bindkey_t *keys;
                    946:        register int i;
                    947: 
                    948:        /* init buffer */
                    949:        buffer = malloc(sizeof(linebuffer_t));
                    950:        if (!buffer) {
                    951:                LOGERR;
                    952:                return NULL;
                    953:        } else {
                    954:                memset(buffer, 0, sizeof(linebuffer_t));
                    955: 
                    956:                buffer->line_in = fin;
                    957:                buffer->line_out = fout;
                    958: 
                    959:                TAILQ_INIT(&buffer->line_history);
                    960:                SLIST_INIT(&buffer->line_cmds);
                    961: 
                    962:                if (prompt) {
                    963:                        buffer->line_prompt = strdup(prompt);
                    964:                        if (!buffer->line_prompt) {
                    965:                                LOGERR;
                    966:                                free(buffer);
                    967:                                return NULL;
                    968:                        } else
                    969:                                buffer->line_eol = buffer->line_bol = strlen(buffer->line_prompt);
                    970:                }
                    971:        }
                    972:        buffer->line_buf = malloc(BUFSIZ);
                    973:        if (!buffer->line_buf) {
                    974:                LOGERR;
                    975:                if (buffer->line_prompt)
                    976:                        free(buffer->line_prompt);
                    977:                free(buffer);
                    978:                return NULL;
                    979:        } else {
                    980:                memset(buffer->line_buf, 0, BUFSIZ);
                    981:                buffer->line_len = 1 + buffer->line_eol;
                    982:        }
                    983:        keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
                    984:        if (!keys) {
                    985:                LOGERR;
                    986:                if (buffer->line_prompt)
                    987:                        free(buffer->line_prompt);
                    988:                free(buffer->line_buf);
                    989:                free(buffer);
                    990:                return NULL;
                    991:        } else
                    992:                memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
                    993: 
                    994:        /* add helper functions */
                    995:        cli_addCommand(buffer, "exit", 0, cli_Cmd_Exit, "exit <cr>", "Exit from console");
                    996:        cli_addCommand(buffer, "help", 0, cli_Cmd_Help, "help [command] <cr>", "Help screen");
                    997:        cli_addCommand(buffer, "-------", 0, NULL, "-------------------------", NULL);
                    998: 
                    999:        /* fill key bindings */
                   1000:        // ascii chars & ctrl+chars
                   1001:        for (i = 0; i < 256; i++) {
                   1002:                *keys[i].key_ch = (u_char) i;
                   1003:                keys[i].key_len = 1;
                   1004: 
                   1005:                if (!i || i == *K_CTRL_D)
                   1006:                        keys[i].key_func = bufEOF;
                   1007:                if (i == *K_CTRL_M || i == *K_CTRL_J)
                   1008:                        keys[i].key_func = bufEOL;
                   1009:                if (i == *K_CTRL_H || i == *K_BACKSPACE)
                   1010:                        keys[i].key_func = bufBS;
                   1011:                if (i == *K_CTRL_C)
                   1012:                        keys[i].key_func = bufCLR;
                   1013:                if (i == *K_CTRL_A)
                   1014:                        keys[i].key_func = bufBEGIN;
                   1015:                if (i == *K_CTRL_E)
                   1016:                        keys[i].key_func = bufEND;
                   1017:                if (i == *K_TAB)
                   1018:                        keys[i].key_func = bufComp;
                   1019:                if (i >= *K_SPACE && i < *K_BACKSPACE)
                   1020:                        keys[i].key_func = bufCHAR;
                   1021:                if (i > *K_BACKSPACE && i < 0xff)
                   1022:                        keys[i].key_func = bufCHAR;
                   1023:                if (i == '?')
                   1024:                        keys[i].key_func = bufHelp;
                   1025:        }
                   1026:        // alt+chars
                   1027:        for (i = 256; i < 512; i++) {
                   1028:                keys[i].key_ch[0] = 0x1b;
                   1029:                keys[i].key_ch[1] = (u_char) i - 256;
                   1030:                keys[i].key_len = 2;
                   1031:        }
                   1032: 
                   1033:        // 3 bytes
                   1034:        keys[i].key_len = sizeof K_F1 - 1;
                   1035:        memcpy(keys[i].key_ch, K_F1, keys[i].key_len);
                   1036:        i++;
                   1037:        keys[i].key_len = sizeof K_F2 - 1;
                   1038:        memcpy(keys[i].key_ch, K_F2, keys[i].key_len);
                   1039:        i++;
                   1040:        keys[i].key_len = sizeof K_F3 - 1;
                   1041:        memcpy(keys[i].key_ch, K_F3, keys[i].key_len);
                   1042:        i++;
                   1043:        keys[i].key_len = sizeof K_F4 - 1;
                   1044:        memcpy(keys[i].key_ch, K_F4, keys[i].key_len);
                   1045:        i++;
                   1046:        keys[i].key_len = sizeof K_CTRL_SH_F1 - 1;
                   1047:        memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i].key_len);
                   1048:        i++;
                   1049:        keys[i].key_len = sizeof K_CTRL_SH_F2 - 1;
                   1050:        memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i].key_len);
                   1051:        i++;
                   1052:        keys[i].key_len = sizeof K_CTRL_SH_F3 - 1;
                   1053:        memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i].key_len);
                   1054:        i++;
                   1055:        keys[i].key_len = sizeof K_CTRL_SH_F4 - 1;
                   1056:        memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i].key_len);
                   1057:        i++;
                   1058:        keys[i].key_len = sizeof K_CTRL_SH_F5 - 1;
                   1059:        memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i].key_len);
                   1060:        i++;
                   1061:        keys[i].key_len = sizeof K_CTRL_SH_F6 - 1;
                   1062:        memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i].key_len);
                   1063:        i++;
                   1064:        keys[i].key_len = sizeof K_CTRL_SH_F7 - 1;
                   1065:        memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i].key_len);
                   1066:        i++;
                   1067:        keys[i].key_len = sizeof K_CTRL_SH_F8 - 1;
                   1068:        memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i].key_len);
                   1069:        i++;
                   1070:        keys[i].key_len = sizeof K_CTRL_SH_F9 - 1;
                   1071:        memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i].key_len);
                   1072:        i++;
                   1073:        keys[i].key_len = sizeof K_CTRL_SH_F10 - 1;
                   1074:        memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i].key_len);
                   1075:        i++;
                   1076:        keys[i].key_len = sizeof K_CTRL_SH_F11 - 1;
                   1077:        memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i].key_len);
                   1078:        i++;
                   1079:        keys[i].key_len = sizeof K_CTRL_SH_F12 - 1;
                   1080:        memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i].key_len);
                   1081:        i++;
                   1082:        keys[i].key_len = sizeof K_CTRL_F1 - 1;
                   1083:        memcpy(keys[i].key_ch, K_CTRL_F1, keys[i].key_len);
                   1084:        i++;
                   1085:        keys[i].key_len = sizeof K_CTRL_F2 - 1;
                   1086:        memcpy(keys[i].key_ch, K_CTRL_F2, keys[i].key_len);
                   1087:        i++;
                   1088:        keys[i].key_len = sizeof K_CTRL_F3 - 1;
                   1089:        memcpy(keys[i].key_ch, K_CTRL_F3, keys[i].key_len);
                   1090:        i++;
                   1091:        keys[i].key_len = sizeof K_CTRL_F4 - 1;
                   1092:        memcpy(keys[i].key_ch, K_CTRL_F4, keys[i].key_len);
                   1093:        i++;
                   1094:        keys[i].key_len = sizeof K_CTRL_F5 - 1;
                   1095:        memcpy(keys[i].key_ch, K_CTRL_F5, keys[i].key_len);
                   1096:        i++;
                   1097:        keys[i].key_len = sizeof K_CTRL_F6 - 1;
                   1098:        memcpy(keys[i].key_ch, K_CTRL_F6, keys[i].key_len);
                   1099:        i++;
                   1100:        keys[i].key_len = sizeof K_CTRL_F7 - 1;
                   1101:        memcpy(keys[i].key_ch, K_CTRL_F7, keys[i].key_len);
                   1102:        i++;
                   1103:        keys[i].key_len = sizeof K_CTRL_F8 - 1;
                   1104:        memcpy(keys[i].key_ch, K_CTRL_F8, keys[i].key_len);
                   1105:        i++;
                   1106:        keys[i].key_len = sizeof K_CTRL_F9 - 1;
                   1107:        memcpy(keys[i].key_ch, K_CTRL_F9, keys[i].key_len);
                   1108:        i++;
                   1109:        keys[i].key_len = sizeof K_CTRL_F10 - 1;
                   1110:        memcpy(keys[i].key_ch, K_CTRL_F10, keys[i].key_len);
                   1111:        i++;
                   1112:        keys[i].key_len = sizeof K_CTRL_F11 - 1;
                   1113:        memcpy(keys[i].key_ch, K_CTRL_F11, keys[i].key_len);
                   1114:        i++;
                   1115:        keys[i].key_len = sizeof K_CTRL_F12 - 1;
                   1116:        memcpy(keys[i].key_ch, K_CTRL_F12, keys[i].key_len);
                   1117:        i++;
                   1118:        keys[i].key_len = sizeof K_HOME - 1;
                   1119:        keys[i].key_func = bufBEGIN;
                   1120:        memcpy(keys[i].key_ch, K_HOME, keys[i].key_len);
                   1121:        i++;
                   1122:        keys[i].key_len = sizeof K_END - 1;
                   1123:        keys[i].key_func = bufEND;
                   1124:        memcpy(keys[i].key_ch, K_END, keys[i].key_len);
                   1125:        i++;
                   1126:        keys[i].key_len = sizeof K_UP - 1;
                   1127:        keys[i].key_func = bufUP;
                   1128:        memcpy(keys[i].key_ch, K_UP, keys[i].key_len);
                   1129:        i++;
                   1130:        keys[i].key_len = sizeof K_DOWN - 1;
                   1131:        keys[i].key_func = bufDOWN;
                   1132:        memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len);
                   1133:        i++;
                   1134:        keys[i].key_len = sizeof K_RIGHT - 1;
                   1135:        keys[i].key_func = bufRIGHT;
                   1136:        memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len);
                   1137:        i++;
                   1138:        keys[i].key_len = sizeof K_LEFT - 1;
                   1139:        keys[i].key_func = bufLEFT;
                   1140:        memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len);
                   1141:        i++;
                   1142:        keys[i].key_len = sizeof K_BTAB - 1;
                   1143:        keys[i].key_func = bufBTAB;
                   1144:        memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len);
                   1145:        i++;
                   1146:        // 4 bytes
                   1147:        keys[i].key_len = sizeof K_INS - 1;
                   1148:        keys[i].key_func = bufMODE;
                   1149:        memcpy(keys[i].key_ch, K_INS, keys[i].key_len);
                   1150:        i++;
                   1151:        keys[i].key_len = sizeof K_DEL - 1;
                   1152:        keys[i].key_func = bufDEL;
                   1153:        memcpy(keys[i].key_ch, K_DEL, keys[i].key_len);
                   1154:        i++;
                   1155:        keys[i].key_len = sizeof K_PGUP - 1;
                   1156:        memcpy(keys[i].key_ch, K_PGUP, keys[i].key_len);
                   1157:        i++;
                   1158:        keys[i].key_len = sizeof K_PGDN - 1;
                   1159:        memcpy(keys[i].key_ch, K_PGDN, keys[i].key_len);
                   1160:        i++;
                   1161:        // 5 bytes
                   1162:        keys[i].key_len = sizeof K_F5 - 1;
                   1163:        memcpy(keys[i].key_ch, K_F5, keys[i].key_len);
                   1164:        i++;
                   1165:        keys[i].key_len = sizeof K_F6 - 1;
                   1166:        memcpy(keys[i].key_ch, K_F6, keys[i].key_len);
                   1167:        i++;
                   1168:        keys[i].key_len = sizeof K_F7 - 1;
                   1169:        memcpy(keys[i].key_ch, K_F7, keys[i].key_len);
                   1170:        i++;
                   1171:        keys[i].key_len = sizeof K_F8 - 1;
                   1172:        memcpy(keys[i].key_ch, K_F8, keys[i].key_len);
                   1173:        i++;
                   1174:        keys[i].key_len = sizeof K_F9 - 1;
                   1175:        memcpy(keys[i].key_ch, K_F9, keys[i].key_len);
                   1176:        i++;
                   1177:        keys[i].key_len = sizeof K_F10 - 1;
                   1178:        memcpy(keys[i].key_ch, K_F10, keys[i].key_len);
                   1179:        i++;
                   1180:        keys[i].key_len = sizeof K_F11 - 1;
                   1181:        memcpy(keys[i].key_ch, K_F11, keys[i].key_len);
                   1182:        i++;
                   1183:        keys[i].key_len = sizeof K_F12 - 1;
                   1184:        memcpy(keys[i].key_ch, K_F12, keys[i].key_len);
                   1185:        i++;
                   1186: 
                   1187:        buffer->line_keys = keys;
                   1188:        return buffer;
1.2       misho    1189: }
                   1190: 
                   1191: /*
1.3       misho    1192:  * cliInitLine() Init CLI input line terminal
                   1193:  * @buffer = CLI buffer
1.2       misho    1194:  * return: none
                   1195: */
1.3       misho    1196: int
                   1197: cliInitLine(linebuffer_t * __restrict buffer)
1.2       misho    1198: {
                   1199:        struct termios t;
                   1200: 
                   1201:        memset(&t, 0, sizeof t);
1.3       misho    1202:        tcgetattr(buffer->line_in, &t);
1.2       misho    1203:        t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
                   1204:        t.c_iflag |= IGNBRK;
                   1205:        t.c_cc[VMIN] = 1;
                   1206:        t.c_cc[VTIME] = 0;
1.3       misho    1207:        return tcsetattr(buffer->line_in, TCSANOW, &t);
                   1208: }
                   1209: 
                   1210: /*
                   1211:  * cliReadLine() Read line from opened CLI session
                   1212:  * @buffer = CLI buffer
                   1213:  * return: NULL if error or !=NULL readed line, must be free after use!
                   1214: */
                   1215: char *
                   1216: cliReadLine(linebuffer_t * __restrict buffer)
                   1217: {
                   1218:        int code, readLen;
                   1219:        register int i;
                   1220:        struct pollfd fds;
                   1221:        char buf[BUFSIZ], *str = NULL;
1.2       misho    1222: 
1.3       misho    1223:        if (!buffer) {
                   1224:                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                   1225:                return NULL;
                   1226:        }
1.2       misho    1227: 
1.3       misho    1228:        memset(&fds, 0, sizeof fds);
                   1229:        fds.fd = buffer->line_in;
                   1230:        fds.events = POLLIN;
                   1231: 
                   1232:        printfCR(buffer, 1);
                   1233:        while (42) {
                   1234:                if (poll(&fds, 1, -1) < 1) {
                   1235:                        LOGERR;
                   1236:                        return str;
                   1237:                }
                   1238: 
                   1239:                memset(buf, 0, sizeof buf);
                   1240:                readLen = read(buffer->line_in, buf, BUFSIZ);
                   1241:                if (readLen == -1) {
                   1242:                        LOGERR;
                   1243:                        return str;
                   1244:                }
                   1245:                if (!readLen) {
                   1246:                        if (buffer->line_buf)
                   1247:                                str = strdup(buffer->line_buf);
                   1248:                        else
                   1249:                                cli_SetErr(EPIPE, "Error:: unknown state ...");
                   1250:                        return str;
                   1251:                }
                   1252: 
                   1253: recheck:
                   1254:                for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
                   1255:                        if (readLen >= buffer->line_keys[i].key_len && 
                   1256:                                        !memcmp(buffer->line_keys[i].key_ch, buf, 
                   1257:                                                buffer->line_keys[i].key_len)) {
                   1258:                                readLen -= buffer->line_keys[i].key_len;
                   1259:                                if (readLen)
                   1260:                                        memmove(buf, buf + buffer->line_keys[i].key_len, readLen);
                   1261:                                else
                   1262:                                        memset(buf, 0, buffer->line_keys[i].key_len);
                   1263: 
                   1264:                                if (buffer->line_keys[i].key_func)
                   1265:                                        if ((code = buffer->line_keys[i].key_func(i, buffer)))
                   1266:                                                readLen = 0;
                   1267: 
                   1268:                                if (readLen)
                   1269:                                        goto recheck;
                   1270:                                else
                   1271:                                        break;
                   1272:                        }
                   1273: 
                   1274:                if (code)
                   1275:                        break;
                   1276:        }
1.2       misho    1277: 
1.3       misho    1278:        if (code != RETCODE_ERR && code != RETCODE_EOF && buffer->line_buf)
                   1279:                str = strdup(buffer->line_buf);
                   1280:        return str;
1.2       misho    1281: }
                   1282: 
1.3       misho    1283: 
1.2       misho    1284: /*
1.3       misho    1285:  * cliNetLoop() CLI network main loop binded to socket
                   1286:  * @buffer = CLI buffer
                   1287:  * @csHistFile = History file name
1.2       misho    1288:  * @sock = client socket
1.3       misho    1289:  * return: RETCODE_ERR error, RETCODE_OK ok
1.2       misho    1290: */
1.3       misho    1291: int
                   1292: cliNetLoop(linebuffer_t * __restrict buffer, const char *csHistFile, int sock)
1.2       misho    1293: {
1.3       misho    1294:        u_char buf[BUFSIZ];
                   1295:        int pid, stat, pty, r, s, alen, flg, attrlen = 0, ret = 0;
1.2       misho    1296:        fd_set fds;
                   1297:        struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
                   1298:        struct telnetAttrs *a, Attr[10];
                   1299: 
1.3       misho    1300:        switch ((pid = forkpty(&pty, NULL, NULL, NULL))) {
1.2       misho    1301:                case -1:
                   1302:                        LOGERR;
                   1303:                        return -1;
                   1304:                case 0:
1.3       misho    1305:                        if (!buffer) {
                   1306:                                cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
                   1307:                                return -1;
                   1308:                        } else
                   1309:                                close(sock);
1.2       misho    1310: 
1.3       misho    1311:                        ret = cliLoop(buffer, csHistFile) < 0 ? 1 : 0;
                   1312:                        cliEnd(buffer);
1.2       misho    1313: 
1.3       misho    1314:                        exit(ret);
1.2       misho    1315:                default:
1.4     ! misho    1316:                        cli_telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
        !          1317:                        cli_telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
        !          1318:                        cli_telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
        !          1319:                        cli_telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
        !          1320:                        cli_telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
        !          1321:                        if ((ret = cli_telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1)
1.2       misho    1322:                                return -1;
1.4     ! misho    1323:                        else
1.2       misho    1324:                                flg = 0;
                   1325: 
                   1326:                        while (42) {
1.3       misho    1327:                                if (waitpid(pid, &stat, WNOHANG))
                   1328:                                        break;
                   1329: 
1.2       misho    1330:                                FD_ZERO(&fds);
                   1331:                                FD_SET(sock, &fds);
                   1332:                                FD_SET(pty, &fds);
                   1333:                                if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
                   1334:                                        if (!ret)
                   1335:                                                cli_SetErr(ETIMEDOUT, "Client session timeout ...");
                   1336: 
                   1337:                                        break;
                   1338:                                }
                   1339: 
                   1340:                                r = FD_ISSET(sock, &fds) ? sock : pty;
                   1341:                                s = FD_ISSET(sock, &fds) ? pty : sock;
                   1342: 
1.3       misho    1343:                                if (FD_ISSET(sock, &fds)) {
                   1344:                                        memset(buf, 0, BUFSIZ);
1.4     ! misho    1345:                                        if ((ret = cli_telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
1.3       misho    1346:                                                if (a)
                   1347:                                                        free(a);
                   1348: 
                   1349:                                                if (-2 == ret)
                   1350:                                                        continue;
                   1351:                                                // EOF
                   1352:                                                if (-3 == ret)
                   1353:                                                        shutdown(sock, SHUT_RD);
                   1354:                                                break;
                   1355:                                        }
                   1356:                                        attrlen = 0;
                   1357:                                        if (1 == flg && alen) {
1.4     ! misho    1358:                                                cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
        !          1359:                                                cli_telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
1.3       misho    1360:                                        }
                   1361:                                        if (2 == flg && alen) {
1.4     ! misho    1362:                                                cli_telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
        !          1363:                                                cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
1.3       misho    1364:                                                                LFLOW_OFF, NULL, 0);
1.4     ! misho    1365:                                                cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
1.3       misho    1366:                                                                LFLOW_RESTART_XON, NULL, 0);
1.4     ! misho    1367:                                                cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
1.3       misho    1368:                                        }
1.2       misho    1369:                                        if (a)
                   1370:                                                free(a);
                   1371: 
1.3       misho    1372:                                        if ((ret = write(pty, buf, ret)) == -1) {
                   1373:                                                LOGERR;
                   1374:                                                break;
                   1375:                                        }
                   1376:                                }
                   1377: 
                   1378:                                if (FD_ISSET(pty, &fds)) {
                   1379:                                        memset(buf, 0, BUFSIZ);
                   1380:                                        if ((ret = read(pty, buf, BUFSIZ)) == -1) {
                   1381:                                                LOGERR;
                   1382:                                                break;
                   1383:                                        }
                   1384: 
1.4     ! misho    1385:                                        if ((ret = cli_telnetSend(sock, Attr, pty == s ? 0 : attrlen, 
        !          1386:                                                                        buf, ret, 0)) == -1)
1.3       misho    1387:                                                break;
1.4     ! misho    1388:                                        else
1.3       misho    1389:                                                flg++;
1.2       misho    1390:                                }
                   1391:                        }
                   1392: 
                   1393:                        close(pty);
                   1394:        }
                   1395: 
                   1396:        return ret;
                   1397: }
                   1398: 
                   1399: /*
1.3       misho    1400:  * cliLoop() CLI main loop
                   1401:  * @buffer = CLI buffer
                   1402:  * @csHistFile = History file name
                   1403:  * return: RETCODE_ERR error, RETCODE_OK ok
1.1       misho    1404: */
1.3       misho    1405: int
                   1406: cliLoop(linebuffer_t * __restrict buffer, const char *csHistFile)
1.1       misho    1407: {
                   1408:        char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
                   1409:        register int i;
1.3       misho    1410:        int ret = RETCODE_OK;
                   1411:        struct tagCommand *cmd;
1.1       misho    1412: 
                   1413:        /* --- main body of CLI --- */
1.3       misho    1414:        cliInitLine(buffer);
1.1       misho    1415: 
1.3       misho    1416:        if (cli_loadHistory(buffer, csHistFile) == RETCODE_ERR)
                   1417:                return RETCODE_ERR;
1.1       misho    1418: 
                   1419:        do {
1.3       misho    1420:                line = cliReadLine(buffer);
                   1421:                if (!line) {
                   1422:                        printfNL(buffer, 0);
1.1       misho    1423:                        break;
1.3       misho    1424:                } else
                   1425:                        cli_addHistory(buffer, NULL);
1.1       misho    1426:                // clear whitespaces
1.4     ! misho    1427:                for (s = line; isspace((int) *s); s++);
1.1       misho    1428:                if (*s) {
1.4     ! misho    1429:                        for (t = s + strlen(s) - 1; t > s && isspace((int) *t); t--);
1.1       misho    1430:                        *++t = 0;
                   1431:                }
                   1432: 
                   1433:                if (*s) {
                   1434:                        memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                   1435:                        for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
                   1436:                                        *app ? app++ : app);
                   1437: 
                   1438:                        // exec_cmd ...
1.3       misho    1439:                        i = 0;
                   1440:                        SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) {
                   1441:                                if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
1.1       misho    1442:                                        break;
1.3       misho    1443:                                else
                   1444:                                        i++;
                   1445:                        }
                   1446: 
1.1       misho    1447:                        if (!cmd) {
1.3       misho    1448:                                cli_Printf(buffer, "\nCommand '%s' not found!\n", items[0]);
1.1       misho    1449:                                ret = -1;
                   1450:                        } else
1.3       misho    1451:                                if (cmd->cmd_func) {
                   1452:                                        cli_Printf(buffer, "\n");
                   1453:                                        ret = cmd->cmd_func(buffer, i, items);
                   1454:                                } else {
                   1455:                                        clrscrEOL(buffer);
                   1456:                                        printfCR(buffer, 1);
                   1457:                                }
1.1       misho    1458:                }
                   1459: 
1.3       misho    1460:                cli_freeLine(buffer);
                   1461:                cli_resetHistory(buffer);
1.1       misho    1462:                free(line);
                   1463:        } while (ret < 1);
                   1464: 
1.3       misho    1465:        cli_saveHistory(buffer, csHistFile, HISTORY_LINES);
1.1       misho    1466:        return ret;
                   1467: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>