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

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

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