tclreadline.c

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
00015  */
00016 
00017 
00018  /* ==================================================================
00019 
00020     FILE: "/home/joze/src/tclreadline/tclreadline.c"
00021     LAST MODIFICATION: "Sat, 25 Mar 2000 21:40:27 +0100 (joze)"
00022     (C) 1998 - 2000 by Johannes Zellner, <johannes@zellner.org>
00023     $Id: tclreadline.c,v 1.5 2006/10/24 17:57:36 demmer Exp $
00024     ---
00025 
00026     tclreadline -- gnu readline for tcl
00027     http://www.zellner.org/tclreadline/
00028     Copyright (c) 1998 - 2000, Johannes Zellner <johannes@zellner.org>
00029 
00030     This software is copyright under the BSD license.
00031 
00032     ================================================================== */
00033 
00034 /*
00035    Copyright (c) 1998 - 2000, Johannes Zellner <johannes@zellner.org>
00036    All rights reserved.
00037    
00038    Redistribution and use in source and binary forms, with or without
00039    modification, are permitted provided that the following conditions
00040    are met:
00041    
00042      * Redistributions of source code must retain the above copyright
00043        notice, this list of conditions and the following disclaimer.
00044      * Redistributions in binary form must reproduce the above copyright
00045        notice, this list of conditions and the following disclaimer in the
00046        documentation and/or other materials provided with the distribution.
00047      * Neither the name of Johannes Zellner nor the names of contributors
00048        to this software may be used to endorse or promote products derived
00049        from this software without specific prior written permission.
00050        
00051    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00052    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00053    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00054    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
00055    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00056    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00057    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00058    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00059    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00060    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00061    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00062 
00063 */
00064 
00065 /*
00066  * Modified by Michael Demmer <demmer@cs.berkeley.edu> to integrate
00067  * into an external configure framework and add the
00068  * "tclreadline::readline eof" command to break the thread of control
00069  * out of the read loop.
00070  */
00071 
00072 //#ifdef HAVE_CONFIG_H
00073 #   include "config.h"
00074 //#endif
00075 
00076 #if TCLREADLINE_ENABLED
00077 
00078 #include <tcl.h>
00079 #include <stdio.h>
00080 #include <stdlib.h>
00081 #include <string.h>
00082 
00083 #if defined (READLINE_LIBRARY)
00084 #   include <readline.h>
00085 #   include <history.h>
00086 #else
00087 #   include <readline/readline.h>
00088 #   include <readline/history.h>
00089 #endif
00090 
00091 #ifdef READLINE_IS_EDITLINE
00092 #define rl_completion_matches completion_matches
00093 #endif
00094 
00099 void rl_extend_line_buffer(int len);
00100 
00101 #ifdef EXECUTING_MACRO_HACK
00102 
00107 extern char* _rl_executing_macro;
00108 #endif
00109 
00110 #include "tclreadline.h"
00111 
00112 #define MALLOC(size) Tcl_Alloc((int) size)
00113 #define FREE(ptr) if (ptr) { Tcl_Free((char*) ptr); ptr = 0; }
00114 
00115 enum {
00116     _CMD_SET     = (1 << 0),
00117     _CMD_GET     = (1 << 1)
00118 };
00119 
00120 
00121 typedef struct cmds_t {
00122     struct cmds_t* prev;
00123     char**         cmd;
00124     struct cmds_t* next;
00125 } cmds_t;
00126 
00127 
00128 #define ISWHITE(c) ((' ' == c) || ('\t' == c) || ('\n' == c))
00129 
00130 /* forward declarations. */
00131 static char* stripleft(char* in);
00132 static char* stripright(char* in);
00133 static char* stripwhite(char* in);
00134 static int TclReadlineLineComplete(void);
00135 static void TclReadlineTerminate(int state);
00136 static char* TclReadlineQuote(char* text, char* quotechars);
00137 static int TclReadlineCmd(ClientData clientData, Tcl_Interp* interp, int argc, CONST char** argv);
00138 static void TclReadlineReadHandler(ClientData clientData, int mask);
00139 static void TclReadlineLineCompleteHandler(char* ptr);
00140 static int TclReadlineInitialize(Tcl_Interp* interp, char* historyfile);
00141 static int blank_line(char* str);
00142 static char** TclReadlineCompletion(char* text, int start, int end);
00143 static char* TclReadline0generator(const char* text, int state);
00144 static char* TclReadlineKnownCommands(char* text, int state, int mode);
00145 static int TclReadlineParse(char** args, int maxargs, char* buf);
00146 
00147 /* must be non-static */
00148 int Tclreadline_SafeInit(Tcl_Interp* interp);
00149 int Tclreadline_Init(Tcl_Interp* interp);
00150 
00151 
00152 enum { 
00153     LINE_PENDING = -1,
00154     LINE_EOF = (1 << 8),
00155     LINE_COMPLETE = (1 << 9)
00156 };
00157 
00161 static int tclrl_state = TCL_OK;
00162 static char* tclrl_eof_string = (char*) NULL;
00163 static char* tclrl_custom_completer = (char*) NULL;
00164 static char* tclrl_last_line = (char*) NULL;
00165 static int tclrl_use_builtin_completer = 1;
00166 static int tclrl_history_length = -1;
00167 Tcl_Interp* tclrl_interp = (Tcl_Interp*) NULL;
00168 
00169 static char* tclrl_license =
00170 "   Copyright (c) 1998 - 2000, Johannes Zellner <johannes@zellner.org>\n"
00171 "   All rights reserved.\n"
00172 "   \n"
00173 "   Redistribution and use in source and binary forms, with or without\n"
00174 "   modification, are permitted provided that the following conditions\n"
00175 "   are met:\n"
00176 "   \n"
00177 "     * Redistributions of source code must retain the above copyright\n"
00178 "       notice, this list of conditions and the following disclaimer.\n"
00179 "     * Redistributions in binary form must reproduce the above copyright\n"
00180 "       notice, this list of conditions and the following disclaimer in the\n"
00181 "       documentation and/or other materials provided with the distribution.\n"
00182 "     * Neither the name of Johannes Zellner nor the names of contributors\n"
00183 "       to this software may be used to endorse or promote products derived\n"
00184 "       from this software without specific prior written permission.\n"
00185 "       \n"
00186 "   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
00187 "   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
00188 "   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
00189 "   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR\n"
00190 "   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n"
00191 "   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n"
00192 "   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n"
00193 "   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n"
00194 "   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n"
00195 "   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"
00196 "   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
00197 
00198 
00199 
00200 static char*
00201 stripleft(char* in)
00202 {
00203     char* ptr = in;
00204     while (*ptr && *ptr <= ' ')
00205         ptr++;
00206     if (in != ptr)
00207         memmove(in, ptr, strlen(ptr) + 1);
00208     return in;
00209 }
00210 
00211 static char*
00212 stripright(char* in)
00213 {
00214     char* ptr;
00215     for (ptr = strchr(in, '\0') - 1; ptr >= in && *ptr <= ' '; ptr--)
00216         *ptr = '\0';
00217     return in;
00218 }
00219 
00220 static char*
00221 stripwhite(char* in)
00222 {
00223     stripleft(in);
00224     stripright(in);
00225     return in;
00226 }
00227 
00228 static int
00229 TclReadlineLineComplete(void)
00230 {
00231     return !(tclrl_state == LINE_PENDING);
00232 }
00233 
00234 static void
00235 TclReadlineTerminate(int state)
00236 {
00237     tclrl_state = state;
00238     rl_callback_handler_remove();
00239 }
00240 
00241 static char*
00242 TclReadlineQuote(char* text, char* quotechars)
00243 {
00244     char* ptr;
00245     char* result_c;
00246     int i, len = strlen(quotechars);
00247     Tcl_DString result;
00248 
00249     Tcl_DStringInit(&result);
00250     for (ptr = text; ptr && *ptr; ptr++) {
00251         for (i = 0; i < len; i++) {
00252             if (quotechars[i] == *ptr) {
00253                 Tcl_DStringAppend(&result, "\\", 1);
00254                 break;
00255             }
00256         }
00257         Tcl_DStringAppend(&result, ptr, 1);
00258     }
00259     result_c = strdup(Tcl_DStringValue(&result));
00260     return result_c;
00261 }
00262 
00263 static int
00264 TclReadlineCmd(
00265     ClientData  clientData,
00266     Tcl_Interp* interp,     /* Current interpreter */
00267     int         argc,       /* Number of arguments */
00268     CONST char** argv        /* Argument strings    */
00269               )
00270 {
00271     int i, obj_idx, status;
00272     Tcl_Obj** objv = (Tcl_Obj**) MALLOC((argc + 1) * sizeof(Tcl_Obj *));
00273 
00274     static const char *subCmds[] = {
00275         "read", "initialize", "write", "add", "complete",
00276         "customcompleter", "builtincompleter", "eofchar",
00277         "reset-terminal", "bell", "eof",
00278         (char *) NULL
00279     };
00280     enum SubCmdIdx {
00281         TCLRL_READ, TCLRL_INITIALIZE, TCLRL_WRITE, TCLRL_ADD, TCLRL_COMPLETE,
00282         TCLRL_CUSTOMCOMPLETER, TCLRL_BUILTINCOMPLETER, TCLRL_EOFCHAR,
00283         TCLRL_RESET_TERMINAL, TCLRL_BELL, TCLRL_EOF
00284     };
00285 
00286     (void)clientData;
00287 
00288     Tcl_ResetResult(interp); /* clear the result space */
00289 
00290     for (i = 0;  i < argc;  i++) {
00291         Tcl_Obj* objPtr = Tcl_NewStringObj(argv[i], -1);
00292         Tcl_IncrRefCount(objPtr);
00293         objv[i] = objPtr;
00294     }
00295     objv[argc] = 0; /* terminate */
00296 
00297     if (argc < 2) {
00298         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
00299         return TCL_ERROR;
00300     }
00301 
00302     status = Tcl_GetIndexFromObj
00303     (interp, objv[1], subCmds, "option", 0, (int *) &obj_idx);
00304 
00305     if (status != TCL_OK) {
00306         FREE(objv)
00307         return status;
00308     }
00309 
00310     switch (obj_idx) {
00311 
00312         case TCLRL_READ:
00313 
00314             if (tclrl_state == LINE_EOF) {
00315                 return TCL_OK;
00316             }
00317 
00318             // The editline wrapper defines the wrong function
00319             // parameter prototype for rl_callback_handler_install, so
00320             // we need to cast below.
00321 #if READLINE_IS_EDITLINE
00322 #define CALLBACK_CAST (void (*)(void))
00323 #else
00324 #define CALLBACK_CAST
00325 #endif
00326             rl_callback_handler_install(argc == 3 ? argv[2] : "%",
00327                 CALLBACK_CAST TclReadlineLineCompleteHandler);
00328 #undef CALLBACK_CAST
00329 
00330             Tcl_CreateFileHandler(0, TCL_READABLE,
00331                 TclReadlineReadHandler, (ClientData) NULL);
00332 
00340             tclrl_state = LINE_PENDING;
00341 
00342             while (!TclReadlineLineComplete()) {
00343 #ifdef EXECUTING_MACRO_HACK
00344 
00350                 if (_rl_executing_macro)
00351                     TclReadlineReadHandler((ClientData) NULL, TCL_READABLE);
00352                 else
00353 #endif
00354                     Tcl_DoOneEvent(TCL_ALL_EVENTS);
00355             }
00356 
00357             Tcl_DeleteFileHandler(0);
00358 
00359             switch (tclrl_state) {
00360 
00361                 case LINE_COMPLETE:
00362 
00363                     return TCL_OK;
00364                     /* NOTREACHED */
00365                     break;
00366 
00367                 case LINE_EOF:
00368                     if (tclrl_eof_string)
00369                         return Tcl_Eval(interp, tclrl_eof_string);
00370                     else
00371                         return TCL_OK;
00372                     /* NOTREACHED */
00373                     break;
00374 
00375                 default:
00376                     return tclrl_state;
00377                     /* NOTREACHED */
00378                     break;
00379             }
00380             break;
00381 
00382         case TCLRL_INITIALIZE:
00383             if (3 != argc) {
00384                 Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
00385                 return TCL_ERROR;
00386             } else {
00387                 return TclReadlineInitialize(interp, (char*)argv[2]);
00388             }
00389             break;
00390 
00391         case TCLRL_WRITE:
00392             if (3 != argc) {
00393                 Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
00394                 return TCL_ERROR;
00395             }  else if (write_history(argv[2])) {
00396                 Tcl_AppendResult(interp, "unable to write history to `",
00397                     argv[2], "'\n", (char*) NULL);
00398                 return TCL_ERROR;
00399             }
00400             if (tclrl_history_length >= 0) {
00401                 history_truncate_file(argv[2], tclrl_history_length);
00402             }
00403             return TCL_OK;
00404             break;
00405 
00406         case TCLRL_ADD:
00407             if (3 != argc) {
00408                 Tcl_WrongNumArgs(interp, 2, objv, "completerLine");
00409                 return TCL_ERROR;
00410             } else if (TclReadlineKnownCommands((char*)argv[2], (int) 0, _CMD_SET)) {
00411                 Tcl_AppendResult(interp, "unable to add command \"",
00412                     argv[2], "\"\n", (char*) NULL);
00413             }
00414             break;
00415 
00416         case TCLRL_COMPLETE:
00417             if (3 != argc) {
00418                 Tcl_WrongNumArgs(interp, 2, objv, "line");
00419                 return TCL_ERROR;
00420             } else if (Tcl_CommandComplete(argv[2])) {
00421                 Tcl_AppendResult(interp, "1", (char*) NULL);
00422             } else {
00423                 Tcl_AppendResult(interp, "0", (char*) NULL);
00424             }
00425             break;
00426 
00427         case TCLRL_CUSTOMCOMPLETER:
00428             if (argc > 3) {
00429                 Tcl_WrongNumArgs(interp, 2, objv, "?scriptCompleter?");
00430                 return TCL_ERROR;
00431             } else if (3 == argc) {
00432                 if (tclrl_custom_completer)
00433                     free(tclrl_custom_completer);
00434                 if (!blank_line((char*)argv[2]))
00435                     tclrl_custom_completer = stripwhite(strdup(argv[2]));
00436             }
00437             Tcl_AppendResult(interp, tclrl_custom_completer, (char*) NULL);
00438             break;
00439 
00440         case TCLRL_BUILTINCOMPLETER:
00441             if (argc > 3) {
00442                 Tcl_WrongNumArgs(interp, 2, objv, "?boolean?");
00443                 return TCL_ERROR;
00444             } else if (3 == argc) {
00445                 int bool = tclrl_use_builtin_completer;
00446                 if (TCL_OK != Tcl_GetBoolean(interp, argv[2], &bool)) {
00447                     Tcl_AppendResult(interp,
00448                         "wrong # args: should be a boolean value.",
00449                         (char*) NULL);
00450                     return TCL_ERROR;
00451                 } else {
00452                     tclrl_use_builtin_completer = bool;
00453                 }
00454             }
00455             Tcl_AppendResult(interp, tclrl_use_builtin_completer ? "1" : "0",
00456                 (char*) NULL);
00457             break;
00458 
00459         case TCLRL_EOFCHAR:
00460             if (argc > 3) {
00461                 Tcl_WrongNumArgs(interp, 2, objv, "?script?");
00462                 return TCL_ERROR;
00463             } else if (3 == argc) {
00464                 if (tclrl_eof_string)
00465                     free(tclrl_eof_string);
00466                 if (!blank_line((char*)argv[2]))
00467                     tclrl_eof_string = stripwhite(strdup(argv[2]));
00468             }
00469             Tcl_AppendResult(interp, tclrl_eof_string, (char*) NULL);
00470             break;
00471 
00472         case TCLRL_RESET_TERMINAL:
00473             /* TODO: add this to the completer */
00474             if (argc > 3) {
00475                 Tcl_WrongNumArgs(interp, 2, objv, "?terminal-name?");
00476                 return TCL_ERROR;
00477             }
00478             if (3 == argc) {
00479                 /*
00480                  * - tcl8.0 doesn't have Tcl_GetString()
00481                  * - rl_reset_terminal() might be defined
00482                  *   to take no arguments. This might produce
00483                  *   a compiler warning.
00484                  */
00485                 rl_reset_terminal(Tcl_GetStringFromObj(objv[2], (int*) NULL));
00486 #ifdef CLEANUP_AFER_SIGNAL
00487             } else {
00488                 rl_cleanup_after_signal();
00489 #endif
00490             }
00491             break;
00492 
00493         case TCLRL_BELL:
00494             /*
00495              * ring the terminal bell obeying the current
00496              * settings -- audible or visible.
00497              */
00498             ding();
00499             break;
00500 
00501         case TCLRL_EOF:
00502             tclrl_state = LINE_EOF;
00503             break;
00504             
00505         default:
00506             goto BAD_COMMAND;
00507             /* NOTREACHED */
00508             break;
00509     }
00510 
00511     return TCL_OK;
00512 
00513 BAD_COMMAND:
00514     Tcl_AppendResult(interp,
00515         "wrong # args: should be \"readline option ?arg ...?\"",
00516         (char*) NULL);
00517     return TCL_ERROR;
00518 
00519 }
00520 
00521 static void
00522 TclReadlineReadHandler(ClientData clientData, int mask)
00523 {
00524     (void)clientData;
00525     if (mask & TCL_READABLE) {
00526 #ifdef EXECUTING_MACRO_HACK
00527         do {
00528 #endif
00529             rl_callback_read_char();
00530 #ifdef EXECUTING_MACRO_HACK
00531 
00536         } while (_rl_executing_macro && !TclReadlineLineComplete());
00537 #endif
00538     }
00539 }
00540 
00541 static void
00542 TclReadlineLineCompleteHandler(char* ptr)
00543 {
00544     if (!ptr) { /* <c-d> */
00545 
00546         TclReadlineTerminate(LINE_EOF);
00547 
00548     } else {
00549 
00550         /*
00551          * From version 0.9.3 upwards, all lines are
00552          * returned, even empty lines. (Only non-empty
00553          * lines are stuffed in readline's history.)
00554          * The calling script is responsible for handling
00555          * empty strings.
00556          */
00557 
00558         char* expansion = (char*) NULL;
00559         int status = history_expand(ptr, &expansion);
00560 
00561         /*
00562          * demmer: the editline wrapper returns a null pointer when
00563          * history_expand is passed an empty string, so we need to
00564          * make it look like what readline returns.
00565          */
00566         if (expansion == NULL && ptr[0] == '\0') {
00567             expansion = strdup("");
00568         }
00569 
00570         if (status >= 1) {
00571             /* TODO: make this a valid tcl output */
00572             printf("%s\n", expansion);
00573         } else if (-1 == status) {
00574             Tcl_AppendResult
00575             (tclrl_interp, "error in history expansion\n", (char*) NULL);
00576             TclReadlineTerminate(TCL_ERROR);
00577         }
00582         Tcl_AppendResult(tclrl_interp, expansion, (char*) NULL);
00583 
00584 #ifdef EXECUTING_MACRO_HACK
00585 
00589         if(!_rl_executing_macro) {
00590 #endif
00591 
00597             if (expansion && *expansion && (!tclrl_last_line ||
00598                     strcmp(tclrl_last_line, expansion))) {
00599                 add_history(expansion);
00600             }
00601             if (tclrl_last_line)
00602                 free(tclrl_last_line);
00603             tclrl_last_line = strdup(expansion);
00604 #ifdef EXECUTING_MACRO_HACK
00605         }
00606 #endif
00607 
00610         TclReadlineTerminate(LINE_COMPLETE);
00611         free(ptr);
00612         free(expansion);
00613     }
00614 }
00615 
00616 int
00617 Tclreadline_SafeInit(Tcl_Interp *interp)
00618 {
00619     return Tclreadline_Init(interp);
00620 }
00621 
00622 int
00623 Tclreadline_Init(Tcl_Interp *interp)
00624 {
00625     int status;
00626     Tcl_CreateCommand(interp, "::tclreadline::readline", TclReadlineCmd,
00627         (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
00628     tclrl_interp = interp;
00629     if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::historyLength",
00630                 (char*) &tclrl_history_length, TCL_LINK_INT)))
00631         return status;
00632 
00633     if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::library",
00634                 (char*) &TCLRL_LIBRARY, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00635         return status;
00636     if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::version",
00637                 (char*) &TCLRL_VERSION, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00638         return status;
00639     if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::patchLevel",
00640                 (char*) &TCLRL_PATCHLEVEL, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00641         return status;
00642     if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::license",
00643                 (char*) &tclrl_license, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00644         return status;
00645 
00646     if (TCL_OK != (status = Tcl_LinkVar(interp, "tclreadline_library",
00647                 (char*) &TCLRL_LIBRARY, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00648         return status;
00649     if (TCL_OK != (status = Tcl_LinkVar(interp, "tclreadline_version",
00650                 (char*) &TCLRL_VERSION, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00651         return status;
00652     if (TCL_OK != (status = Tcl_LinkVar(interp, "tclreadline_patchLevel",
00653                 (char*) &TCLRL_PATCHLEVEL, TCL_LINK_STRING | TCL_LINK_READ_ONLY)))
00654         return status;
00655 
00656     return Tcl_PkgProvide(interp, "tclreadline", TCLRL_VERSION);
00657 }
00658 
00659 static int
00660 TclReadlineInitialize(Tcl_Interp* interp, char* historyfile)
00661 {
00662     rl_readline_name = "tclreadline";
00663     /*    rl_special_prefixes = "${\"["; */
00664     rl_special_prefixes = "$";
00673     /* 11.Sep rl_basic_word_break_characters = " \t\n\"\\@$}=;|&[]"; */
00674     /* besser (11. Sept) 2. (removed \") */
00675     /* rl_basic_word_break_characters = " \t\n\\@$}=;|&[]"; */
00676     /* besser (11. Sept) 3. (removed }) */
00677     rl_basic_word_break_characters = " \t\n\\@$=;|&[]";
00678 #if 0
00679     rl_basic_quote_characters = "\"{"; /* XXX ??? XXX */
00680     rl_completer_quote_characters = "\"";
00681 #endif
00682     /*
00683        rl_filename_quote_characters
00684        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00685 
00686        rl_filename_quoting_function
00687        = (CPFunction*) TclReadlineFilenameQuotingFunction;
00688      */
00689     /*
00690        rl_filename_quoting_desired = 1;
00691      */
00692 
00693     using_history();
00694     if (!tclrl_eof_string)
00695         tclrl_eof_string = strdup("puts {}; exit");
00696 
00697     /*
00698      * try to read historyfile in home
00699      * directory. If this failes, this
00700      * is *not* an error.
00701      */
00702     rl_attempted_completion_function = (CPPFunction *) TclReadlineCompletion;
00703     if (read_history(historyfile)) {
00704         if (write_history(historyfile)) {
00705             Tcl_AppendResult (interp, "warning: `",
00706                 historyfile, "' is not writable.", (char*) NULL);
00707         }
00708     }
00709     return TCL_OK;
00710 }
00711 
00712 static int
00713 blank_line(char* str)
00714 {
00715     char* ptr;
00716     for (ptr = str; ptr && *ptr; ptr++) {
00717         if (!ISWHITE(*ptr))
00718             return 0;
00719     }
00720     return 1;
00721 }
00722 
00723 static char**
00724 TclReadlineCompletion(char* text, int start, int end)
00725 {
00726     char** matches = (char**) NULL;
00727     int status;
00728     rl_completion_append_character = ' '; /* reset, just in case ... */
00729 
00730     if (text && ('!' == text[0]
00731             || (start && rl_line_buffer[start - 1] == '!' /* for '$' */))) {
00732         char* expansion = (char*) NULL;
00733         int oldlen = strlen(rl_line_buffer);
00734         status = history_expand(rl_line_buffer, &expansion);
00735         if (status >= 1) {
00736             rl_extend_line_buffer(strlen(expansion) + 1);
00737             strcpy(rl_line_buffer, expansion);
00738             rl_end = strlen(expansion);
00739             rl_point += strlen(expansion) - oldlen;
00740             free(expansion);
00741             /*
00742              * TODO:
00743              * because we return 0 == matches,
00744              * the filename completer will still beep.
00745              rl_inhibit_completion = 1;
00746              */
00747             return matches;
00748         }
00749         free(expansion);
00750     }
00751 
00752     if (tclrl_custom_completer) {
00753         char start_s[BUFSIZ], end_s[BUFSIZ];
00754         Tcl_Obj* obj;
00755         Tcl_Obj** objv;
00756         int objc;
00757         int state;
00758         char* quoted_text = TclReadlineQuote(text, "$[]{}\"");
00759         char* quoted_rl_line_buffer
00760         = TclReadlineQuote(rl_line_buffer, "$[]{}\"");
00761         sprintf(start_s, "%d", start);
00762         sprintf(end_s, "%d", end);
00763         Tcl_ResetResult(tclrl_interp); /* clear result space */
00764         state = Tcl_VarEval(tclrl_interp, tclrl_custom_completer,
00765             " \"", quoted_text, "\" ", start_s, " ", end_s,
00766             " \"", quoted_rl_line_buffer, "\"", (char*) NULL);
00767         free(quoted_text);
00768         free(quoted_rl_line_buffer);
00769         if (TCL_OK != state) {
00770             Tcl_AppendResult (tclrl_interp, " `", tclrl_custom_completer,
00771                 " \"", quoted_text, "\" ", start_s, " ", end_s,
00772                 " \"", quoted_rl_line_buffer, "\"' failed.", (char*) NULL);
00773             TclReadlineTerminate(state);
00774             return matches;
00775         }
00776         obj = Tcl_GetObjResult(tclrl_interp);
00777         status = Tcl_ListObjGetElements(tclrl_interp, obj, &objc, &objv);
00778         if (TCL_OK != status)
00779             return matches;
00780 
00781         if (objc) {
00782             int i, length;
00783             matches = (char**) malloc(sizeof(char*) * (objc + 1));
00784             for (i = 0; i < objc; i++) {
00785                 matches[i] = strdup(Tcl_GetStringFromObj(objv[i], &length));
00786                 if (1 == objc && !strlen(matches[i])) {
00787                     free(matches[i]);
00788                     free(matches);
00789                     Tcl_ResetResult(tclrl_interp); /* clear result space */
00790                     return (char**) NULL;
00791                 }
00792             }
00793 
00801             if (2 == objc && !strlen(matches[1])) {
00802                 i--;
00803                 free(matches[1]);
00804                 rl_completion_append_character = '\0';
00805             }
00806 
00807             matches[i] = (char*) NULL; /* terminate */
00808         }
00809         Tcl_ResetResult(tclrl_interp); /* clear result space */
00810     }
00811 
00812     if (!matches && tclrl_use_builtin_completer) {
00813         matches = rl_completion_matches(text, TclReadline0generator);
00814     }
00815 
00816     return matches;
00817 }
00818 
00819 static char*
00820 TclReadline0generator(const char* text, int state)
00821 {
00822     return TclReadlineKnownCommands((char*)text, state, _CMD_GET);
00823 }
00824 
00825 static char*
00826 TclReadlineKnownCommands(char* text, int state, int mode)
00827 {
00828     static int len;
00829     static cmds_t *cmds = (cmds_t *) NULL, *new;
00830     char* tmp;
00831     char* args[256];
00832     int i, argc;
00833     char** name;
00834 
00835     char* local_line = (char*) NULL;
00836     int sub;
00837 
00838 
00839     switch (mode) {
00840 
00841         case _CMD_SET:
00842 
00843             new = (cmds_t *) MALLOC(sizeof(cmds_t));
00844             new->next = (cmds_t *) NULL;
00845 
00846             if (!cmds) {
00847                 cmds = new;
00848                 cmds->prev = new;
00849             }
00850             else {
00851                 cmds->prev->next = new;
00852                 cmds->prev = new;
00853             }
00854 
00855             tmp = strdup(text);
00856             argc = TclReadlineParse(args, sizeof(args), tmp);
00857 
00858             new->cmd = (char**) MALLOC(sizeof(char*) * (argc + 1));
00859 
00860             for (i = 0; i < argc; i++)
00861                 new->cmd[i] = args[i];
00862 
00863             new->cmd[argc] = (char*) NULL;
00864 
00865             return (char*) NULL;
00866             break;
00867 
00868 
00869         case _CMD_GET:
00870 
00871             local_line = strdup(rl_line_buffer);
00872             sub = TclReadlineParse(args, sizeof(args), local_line);
00873 
00874             if (0 == sub || (1 == sub && '\0' != text[0])) {
00875                 if (!state) {
00876                     new = cmds;
00877                     len = strlen(text);
00878                 }
00879                 while (new && (name = new->cmd)) {
00880                     new = new->next;
00881                     if (!strncmp(name[0], text, len))
00882                         return strdup(name[0]);
00883                 }
00884                 return (char*) NULL;
00885             } else {
00886 
00887                 if (!state) {
00888 
00889                     new = cmds;
00890                     len = strlen(text);
00891 
00892                     while (new && (name = new->cmd)) {
00893                         if (!strcmp(name[0], args[0]))
00894                             break;
00895                         new = new->next;
00896                     }
00897 
00898                     if (!new)
00899                         return (char*) NULL;
00900 
00901                     for (i = 0; new->cmd[i]; i++) /* EMPTY */;
00902 
00903                     if (sub < i && !strncmp(new->cmd[sub], text, len))
00904                         return strdup(new->cmd[sub]);
00905                     else
00906                         return (char*) NULL;
00907 
00908                 }
00909                 else
00910                     return (char*) NULL;
00911 
00912                 /* NOTREACHED */
00913                 break;
00914             }
00915 
00916 
00917         default:
00918             return (char*) NULL;
00919             break;
00920 
00921     }
00922     /* NOTREACHED */
00923 }
00924 
00925 static int
00926 TclReadlineParse(char** args, int maxargs, char* buf)
00927 {
00928     int nr = 0;
00929 
00930     while (*buf != '\0' && nr < maxargs) {
00931         /*
00932          * Strip whitespace.  Use nulls, so
00933          * that the previous argument is terminated
00934          * automatically.
00935          */
00936         while (ISWHITE(*buf))
00937             *buf++ = '\0';
00938 
00939         if (!(*buf)) /* don't count the terminating NULL */
00940             break;
00941 
00942         *args++ = buf;
00943         nr++;
00944 
00945         while (('\0' != *buf) && !ISWHITE(*buf))
00946             buf++;
00947     }
00948 
00949     *args = '\0';
00950     return nr;
00951 }
00952 
00953 #endif /* TCLREADLINE_ENABLED */

Generated on Sat Sep 8 08:43:35 2007 for DTN Reference Implementation by  doxygen 1.5.3