tclTest.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:188k
源码类别:

通讯编程

开发平台:

Visual C++

  1.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  2. {
  3.     int i, ii, indices, stringLength, match, about;
  4.     int hasxflags, cflags, eflags;
  5.     Tcl_RegExp regExpr;
  6.     char *string;
  7.     Tcl_Obj *objPtr;
  8.     Tcl_RegExpInfo info;
  9.     static CONST char *options[] = {
  10. "-indices", "-nocase", "-about", "-expanded",
  11. "-line", "-linestop", "-lineanchor",
  12. "-xflags",
  13. "--", (char *) NULL
  14.     };
  15.     enum options {
  16. REGEXP_INDICES, REGEXP_NOCASE, REGEXP_ABOUT, REGEXP_EXPANDED,
  17. REGEXP_MULTI, REGEXP_NOCROSS, REGEXP_NEWL,
  18. REGEXP_XFLAGS,
  19. REGEXP_LAST
  20.     };
  21.     indices = 0;
  22.     about = 0;
  23.     cflags = REG_ADVANCED;
  24.     eflags = 0;
  25.     hasxflags = 0;
  26.     
  27.     for (i = 1; i < objc; i++) {
  28. char *name;
  29. int index;
  30. name = Tcl_GetString(objv[i]);
  31. if (name[0] != '-') {
  32.     break;
  33. }
  34. if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
  35. &index) != TCL_OK) {
  36.     return TCL_ERROR;
  37. }
  38. switch ((enum options) index) {
  39.     case REGEXP_INDICES: {
  40. indices = 1;
  41. break;
  42.     }
  43.     case REGEXP_NOCASE: {
  44. cflags |= REG_ICASE;
  45. break;
  46.     }
  47.     case REGEXP_ABOUT: {
  48. about = 1;
  49. break;
  50.     }
  51.     case REGEXP_EXPANDED: {
  52. cflags |= REG_EXPANDED;
  53. break;
  54.     }
  55.     case REGEXP_MULTI: {
  56. cflags |= REG_NEWLINE;
  57. break;
  58.     }
  59.     case REGEXP_NOCROSS: {
  60. cflags |= REG_NLSTOP;
  61. break;
  62.     }
  63.     case REGEXP_NEWL: {
  64. cflags |= REG_NLANCH;
  65. break;
  66.     }
  67.     case REGEXP_XFLAGS: {
  68. hasxflags = 1;
  69. break;
  70.     }
  71.     case REGEXP_LAST: {
  72. i++;
  73. goto endOfForLoop;
  74.     }
  75. }
  76.     }
  77.     endOfForLoop:
  78.     if (objc - i < hasxflags + 2 - about) {
  79. Tcl_WrongNumArgs(interp, 1, objv,
  80. "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
  81. return TCL_ERROR;
  82.     }
  83.     objc -= i;
  84.     objv += i;
  85.     if (hasxflags) {
  86. string = Tcl_GetStringFromObj(objv[0], &stringLength);
  87. TestregexpXflags(string, stringLength, &cflags, &eflags);
  88. objc--;
  89. objv++;
  90.     }
  91.     regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
  92.     if (regExpr == NULL) {
  93. return TCL_ERROR;
  94.     }
  95.     objPtr = objv[1];
  96.     if (about) {
  97. if (TclRegAbout(interp, regExpr) < 0) {
  98.     return TCL_ERROR;
  99. }
  100. return TCL_OK;
  101.     }
  102.     match = Tcl_RegExpExecObj(interp, regExpr, objPtr, 0 /* offset */,
  103.     objc-2 /* nmatches */, eflags);
  104.     if (match < 0) {
  105. return TCL_ERROR;
  106.     }
  107.     if (match == 0) {
  108. /*
  109.  * Set the interpreter's object result to an integer object w/
  110.  * value 0. 
  111.  */
  112. Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
  113. if (objc > 2 && (cflags&REG_EXPECT) && indices) {
  114.     char *varName;
  115.     CONST char *value;
  116.     int start, end;
  117.     char resinfo[TCL_INTEGER_SPACE * 2];
  118.     varName = Tcl_GetString(objv[2]);
  119.     TclRegExpRangeUniChar(regExpr, -1, &start, &end);
  120.     sprintf(resinfo, "%d %d", start, end-1);
  121.     value = Tcl_SetVar(interp, varName, resinfo, 0);
  122.     if (value == NULL) {
  123. Tcl_AppendResult(interp, "couldn't set variable "",
  124. varName, """, (char *) NULL);
  125. return TCL_ERROR;
  126.     }
  127. } else if (cflags & TCL_REG_CANMATCH) {
  128.     char *varName;
  129.     CONST char *value;
  130.     char resinfo[TCL_INTEGER_SPACE * 2];
  131.     Tcl_RegExpGetInfo(regExpr, &info);
  132.     varName = Tcl_GetString(objv[2]);
  133.     sprintf(resinfo, "%ld", info.extendStart);
  134.     value = Tcl_SetVar(interp, varName, resinfo, 0);
  135.     if (value == NULL) {
  136. Tcl_AppendResult(interp, "couldn't set variable "",
  137. varName, """, (char *) NULL);
  138. return TCL_ERROR;
  139.     }
  140. }
  141. return TCL_OK;
  142.     }
  143.     /*
  144.      * If additional variable names have been specified, return
  145.      * index information in those variables.
  146.      */
  147.     objc -= 2;
  148.     objv += 2;
  149.     Tcl_RegExpGetInfo(regExpr, &info);
  150.     for (i = 0; i < objc; i++) {
  151. int start, end;
  152. Tcl_Obj *newPtr, *varPtr, *valuePtr;
  153. varPtr = objv[i];
  154. ii = ((cflags&REG_EXPECT) && i == objc-1) ? -1 : i;
  155. if (indices) {
  156.     Tcl_Obj *objs[2];
  157.     if (ii == -1) {
  158. TclRegExpRangeUniChar(regExpr, ii, &start, &end);
  159.     } else if (ii > info.nsubs) {
  160. start = -1;
  161. end = -1;
  162.     } else {
  163. start = info.matches[ii].start;
  164. end = info.matches[ii].end;
  165.     }
  166.     /*
  167.      * Adjust index so it refers to the last character in the
  168.      * match instead of the first character after the match.
  169.      */
  170.     
  171.     if (end >= 0) {
  172. end--;
  173.     }
  174.     objs[0] = Tcl_NewLongObj(start);
  175.     objs[1] = Tcl_NewLongObj(end);
  176.     newPtr = Tcl_NewListObj(2, objs);
  177. } else {
  178.     if (ii == -1) {
  179. TclRegExpRangeUniChar(regExpr, ii, &start, &end);
  180. newPtr = Tcl_GetRange(objPtr, start, end);
  181.     } else if (ii > info.nsubs) {
  182. newPtr = Tcl_NewObj();
  183.     } else {
  184. newPtr = Tcl_GetRange(objPtr, info.matches[ii].start,
  185. info.matches[ii].end - 1);
  186.     }
  187. }
  188. Tcl_IncrRefCount(newPtr);
  189. valuePtr = Tcl_ObjSetVar2(interp, varPtr, NULL, newPtr, 0);
  190. Tcl_DecrRefCount(newPtr);
  191. if (valuePtr == NULL) {
  192.     Tcl_AppendResult(interp, "couldn't set variable "",
  193.     Tcl_GetString(varPtr), """, (char *) NULL);
  194.     return TCL_ERROR;
  195. }
  196.     }
  197.     /*
  198.      * Set the interpreter's object result to an integer object w/ value 1. 
  199.      */
  200.     Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
  201.     return TCL_OK;
  202. }
  203. /*
  204.  *---------------------------------------------------------------------------
  205.  *
  206.  * TestregexpXflags --
  207.  *
  208.  * Parse a string of extended regexp flag letters, for testing.
  209.  *
  210.  * Results:
  211.  * No return value (you're on your own for errors here).
  212.  *
  213.  * Side effects:
  214.  * Modifies *cflagsPtr, a regcomp flags word, and *eflagsPtr, a
  215.  * regexec flags word, as appropriate.
  216.  *
  217.  *----------------------------------------------------------------------
  218.  */
  219. static void
  220. TestregexpXflags(string, length, cflagsPtr, eflagsPtr)
  221.     char *string; /* The string of flags. */
  222.     int length; /* The length of the string in bytes. */
  223.     int *cflagsPtr; /* compile flags word */
  224.     int *eflagsPtr; /* exec flags word */
  225. {
  226.     int i;
  227.     int cflags;
  228.     int eflags;
  229.     cflags = *cflagsPtr;
  230.     eflags = *eflagsPtr;
  231.     for (i = 0; i < length; i++) {
  232. switch (string[i]) {
  233.     case 'a': {
  234. cflags |= REG_ADVF;
  235. break;
  236.     }
  237.     case 'b': {
  238. cflags &= ~REG_ADVANCED;
  239. break;
  240.     }
  241.     case 'c': {
  242. cflags |= TCL_REG_CANMATCH;
  243. break;
  244.     }
  245.     case 'e': {
  246. cflags &= ~REG_ADVANCED;
  247. cflags |= REG_EXTENDED;
  248. break;
  249.     }
  250.     case 'q': {
  251. cflags &= ~REG_ADVANCED;
  252. cflags |= REG_QUOTE;
  253. break;
  254.     }
  255.     case 'o': { /* o for opaque */
  256. cflags |= REG_NOSUB;
  257. break;
  258.     }
  259.     case 's': { /* s for start */
  260. cflags |= REG_BOSONLY;
  261. break;
  262.     }
  263.     case '+': {
  264. cflags |= REG_FAKE;
  265. break;
  266.     }
  267.     case ',': {
  268. cflags |= REG_PROGRESS;
  269. break;
  270.     }
  271.     case '.': {
  272. cflags |= REG_DUMP;
  273. break;
  274.     }
  275.     case ':': {
  276. eflags |= REG_MTRACE;
  277. break;
  278.     }
  279.     case ';': {
  280. eflags |= REG_FTRACE;
  281. break;
  282.     }
  283.     case '^': {
  284. eflags |= REG_NOTBOL;
  285. break;
  286.     }
  287.     case '$': {
  288. eflags |= REG_NOTEOL;
  289. break;
  290.     }
  291.     case 't': {
  292. cflags |= REG_EXPECT;
  293. break;
  294.     }
  295.     case '%': {
  296. eflags |= REG_SMALL;
  297. break;
  298.     }
  299. }
  300.     }
  301.     *cflagsPtr = cflags;
  302.     *eflagsPtr = eflags;
  303. }
  304. /*
  305.  *----------------------------------------------------------------------
  306.  *
  307.  * TestsetassocdataCmd --
  308.  *
  309.  * This procedure implements the "testsetassocdata" command. It is used
  310.  * to test Tcl_SetAssocData.
  311.  *
  312.  * Results:
  313.  * A standard Tcl result.
  314.  *
  315.  * Side effects:
  316.  * Modifies or creates an association between a key and associated
  317.  * data for this interpreter.
  318.  *
  319.  *----------------------------------------------------------------------
  320.  */
  321. static int
  322. TestsetassocdataCmd(clientData, interp, argc, argv)
  323.     ClientData clientData; /* Not used. */
  324.     Tcl_Interp *interp; /* Current interpreter. */
  325.     int argc; /* Number of arguments. */
  326.     CONST char **argv; /* Argument strings. */
  327. {
  328.     char *buf;
  329.     char *oldData;
  330.     Tcl_InterpDeleteProc *procPtr;
  331.     
  332.     if (argc != 3) {
  333.         Tcl_AppendResult(interp, "wrong # arguments: should be "", argv[0],
  334.                 " data_key data_item"", (char *) NULL);
  335.         return TCL_ERROR;
  336.     }
  337.     buf = ckalloc((unsigned) strlen(argv[2]) + 1);
  338.     strcpy(buf, argv[2]);
  339.     /*
  340.      * If we previously associated a malloced value with the variable,
  341.      * free it before associating a new value.
  342.      */
  343.     oldData = (char *) Tcl_GetAssocData(interp, argv[1], &procPtr);
  344.     if ((oldData != NULL) && (procPtr == CleanupTestSetassocdataTests)) {
  345. ckfree(oldData);
  346.     }
  347.     
  348.     Tcl_SetAssocData(interp, argv[1], CleanupTestSetassocdataTests, 
  349. (ClientData) buf);
  350.     return TCL_OK;
  351. }
  352. /*
  353.  *----------------------------------------------------------------------
  354.  *
  355.  * TestsetplatformCmd --
  356.  *
  357.  * This procedure implements the "testsetplatform" command. It is
  358.  * used to change the tclPlatform global variable so all file
  359.  * name conversions can be tested on a single platform.
  360.  *
  361.  * Results:
  362.  * A standard Tcl result.
  363.  *
  364.  * Side effects:
  365.  * Sets the tclPlatform global variable.
  366.  *
  367.  *----------------------------------------------------------------------
  368.  */
  369. static int
  370. TestsetplatformCmd(clientData, interp, argc, argv)
  371.     ClientData clientData; /* Not used. */
  372.     Tcl_Interp *interp; /* Current interpreter. */
  373.     int argc; /* Number of arguments. */
  374.     CONST char **argv; /* Argument strings. */
  375. {
  376.     size_t length;
  377.     TclPlatformType *platform;
  378. #ifdef __WIN32__
  379.     platform = TclWinGetPlatform();
  380. #else
  381.     platform = &tclPlatform;
  382. #endif
  383.     
  384.     if (argc != 2) {
  385.         Tcl_AppendResult(interp, "wrong # arguments: should be "", argv[0],
  386.                 " platform"", (char *) NULL);
  387.         return TCL_ERROR;
  388.     }
  389.     length = strlen(argv[1]);
  390.     if (strncmp(argv[1], "unix", length) == 0) {
  391. *platform = TCL_PLATFORM_UNIX;
  392.     } else if (strncmp(argv[1], "mac", length) == 0) {
  393. *platform = TCL_PLATFORM_MAC;
  394.     } else if (strncmp(argv[1], "windows", length) == 0) {
  395. *platform = TCL_PLATFORM_WINDOWS;
  396.     } else {
  397.         Tcl_AppendResult(interp, "unsupported platform: should be one of ",
  398. "unix, mac, or windows", (char *) NULL);
  399. return TCL_ERROR;
  400.     }
  401.     return TCL_OK;
  402. }
  403. /*
  404.  *----------------------------------------------------------------------
  405.  *
  406.  * TeststaticpkgCmd --
  407.  *
  408.  * This procedure implements the "teststaticpkg" command.
  409.  * It is used to test the procedure Tcl_StaticPackage.
  410.  *
  411.  * Results:
  412.  * A standard Tcl result.
  413.  *
  414.  * Side effects:
  415.  * When the packge given by argv[1] is loaded into an interpeter,
  416.  * variable "x" in that interpreter is set to "loaded".
  417.  *
  418.  *----------------------------------------------------------------------
  419.  */
  420. static int
  421. TeststaticpkgCmd(dummy, interp, argc, argv)
  422.     ClientData dummy; /* Not used. */
  423.     Tcl_Interp *interp; /* Current interpreter. */
  424.     int argc; /* Number of arguments. */
  425.     CONST char **argv; /* Argument strings. */
  426. {
  427.     int safe, loaded;
  428.     if (argc != 4) {
  429. Tcl_AppendResult(interp, "wrong # arguments: should be "",
  430. argv[0], " pkgName safe loaded"", (char *) NULL);
  431. return TCL_ERROR;
  432.     }
  433.     if (Tcl_GetInt(interp, argv[2], &safe) != TCL_OK) {
  434. return TCL_ERROR;
  435.     }
  436.     if (Tcl_GetInt(interp, argv[3], &loaded) != TCL_OK) {
  437. return TCL_ERROR;
  438.     }
  439.     Tcl_StaticPackage((loaded) ? interp : NULL, argv[1], StaticInitProc,
  440.     (safe) ? StaticInitProc : NULL);
  441.     return TCL_OK;
  442. }
  443. static int
  444. StaticInitProc(interp)
  445.     Tcl_Interp *interp; /* Interpreter in which package
  446.  * is supposedly being loaded. */
  447. {
  448.     Tcl_SetVar(interp, "x", "loaded", TCL_GLOBAL_ONLY);
  449.     return TCL_OK;
  450. }
  451. /*
  452.  *----------------------------------------------------------------------
  453.  *
  454.  * TesttranslatefilenameCmd --
  455.  *
  456.  * This procedure implements the "testtranslatefilename" command.
  457.  * It is used to test the Tcl_TranslateFileName command.
  458.  *
  459.  * Results:
  460.  * A standard Tcl result.
  461.  *
  462.  * Side effects:
  463.  * None.
  464.  *
  465.  *----------------------------------------------------------------------
  466.  */
  467. static int
  468. TesttranslatefilenameCmd(dummy, interp, argc, argv)
  469.     ClientData dummy; /* Not used. */
  470.     Tcl_Interp *interp; /* Current interpreter. */
  471.     int argc; /* Number of arguments. */
  472.     CONST char **argv; /* Argument strings. */
  473. {
  474.     Tcl_DString buffer;
  475.     CONST char *result;
  476.     if (argc != 2) {
  477. Tcl_AppendResult(interp, "wrong # arguments: should be "",
  478. argv[0], " path"", (char *) NULL);
  479. return TCL_ERROR;
  480.     }
  481.     result = Tcl_TranslateFileName(interp, argv[1], &buffer);
  482.     if (result == NULL) {
  483. return TCL_ERROR;
  484.     }
  485.     Tcl_AppendResult(interp, result, NULL);
  486.     Tcl_DStringFree(&buffer);
  487.     return TCL_OK;
  488. }
  489. /*
  490.  *----------------------------------------------------------------------
  491.  *
  492.  * TestupvarCmd --
  493.  *
  494.  * This procedure implements the "testupvar2" command.  It is used
  495.  * to test Tcl_UpVar and Tcl_UpVar2.
  496.  *
  497.  * Results:
  498.  * A standard Tcl result.
  499.  *
  500.  * Side effects:
  501.  * Creates or modifies an "upvar" reference.
  502.  *
  503.  *----------------------------------------------------------------------
  504.  */
  505. /* ARGSUSED */
  506. static int
  507. TestupvarCmd(dummy, interp, argc, argv)
  508.     ClientData dummy; /* Not used. */
  509.     Tcl_Interp *interp; /* Current interpreter. */
  510.     int argc; /* Number of arguments. */
  511.     CONST char **argv; /* Argument strings. */
  512. {
  513.     int flags = 0;
  514.     
  515.     if ((argc != 5) && (argc != 6)) {
  516. Tcl_AppendResult(interp, "wrong # arguments: should be "",
  517. argv[0], " level name ?name2? dest global"", (char *) NULL);
  518. return TCL_ERROR;
  519.     }
  520.     if (argc == 5) {
  521. if (strcmp(argv[4], "global") == 0) {
  522.     flags = TCL_GLOBAL_ONLY;
  523. } else if (strcmp(argv[4], "namespace") == 0) {
  524.     flags = TCL_NAMESPACE_ONLY;
  525. }
  526. return Tcl_UpVar(interp, argv[1], argv[2], argv[3], flags);
  527.     } else {
  528. if (strcmp(argv[5], "global") == 0) {
  529.     flags = TCL_GLOBAL_ONLY;
  530. } else if (strcmp(argv[5], "namespace") == 0) {
  531.     flags = TCL_NAMESPACE_ONLY;
  532. }
  533. return Tcl_UpVar2(interp, argv[1], argv[2], 
  534. (argv[3][0] == 0) ? (char *) NULL : argv[3], argv[4],
  535. flags);
  536.     }
  537. }
  538. /*
  539.  *----------------------------------------------------------------------
  540.  *
  541.  * TestseterrorcodeCmd --
  542.  *
  543.  * This procedure implements the "testseterrorcodeCmd".
  544.  * This tests up to five elements passed to the
  545.  * Tcl_SetErrorCode command.
  546.  *
  547.  * Results:
  548.  * A standard Tcl result. Always returns TCL_ERROR so that
  549.  * the error code can be tested.
  550.  *
  551.  * Side effects:
  552.  * None.
  553.  *
  554.  *----------------------------------------------------------------------
  555.  */
  556. /* ARGSUSED */
  557. static int
  558. TestseterrorcodeCmd(dummy, interp, argc, argv)
  559.     ClientData dummy; /* Not used. */
  560.     Tcl_Interp *interp; /* Current interpreter. */
  561.     int argc; /* Number of arguments. */
  562.     CONST char **argv; /* Argument strings. */
  563. {
  564.     if (argc > 6) {
  565. Tcl_SetResult(interp, "too many args", TCL_STATIC);
  566. return TCL_ERROR;
  567.     }
  568.     Tcl_SetErrorCode(interp, argv[1], argv[2], argv[3], argv[4],
  569.     argv[5], NULL);
  570.     return TCL_ERROR;
  571. }
  572. /*
  573.  *----------------------------------------------------------------------
  574.  *
  575.  * TestsetobjerrorcodeCmd --
  576.  *
  577.  * This procedure implements the "testsetobjerrorcodeCmd".
  578.  * This tests the Tcl_SetObjErrorCode function.
  579.  *
  580.  * Results:
  581.  * A standard Tcl result. Always returns TCL_ERROR so that
  582.  * the error code can be tested.
  583.  *
  584.  * Side effects:
  585.  * None.
  586.  *
  587.  *----------------------------------------------------------------------
  588.  */
  589. /* ARGSUSED */
  590. static int
  591. TestsetobjerrorcodeCmd(dummy, interp, objc, objv)
  592.     ClientData dummy; /* Not used. */
  593.     Tcl_Interp *interp; /* Current interpreter. */
  594.     int objc; /* Number of arguments. */
  595.     Tcl_Obj *CONST objv[]; /* The argument objects. */
  596. {
  597.     Tcl_Obj *listObjPtr;
  598.     if (objc > 1) {
  599. listObjPtr = Tcl_ConcatObj(objc - 1, objv + 1);
  600.     } else {
  601. listObjPtr = Tcl_NewObj();
  602.     }
  603.     Tcl_IncrRefCount(listObjPtr);
  604.     Tcl_SetObjErrorCode(interp, listObjPtr);
  605.     Tcl_DecrRefCount(listObjPtr);
  606.     return TCL_ERROR;
  607. }
  608. /*
  609.  *----------------------------------------------------------------------
  610.  *
  611.  * TestfeventCmd --
  612.  *
  613.  * This procedure implements the "testfevent" command.  It is
  614.  * used for testing the "fileevent" command.
  615.  *
  616.  * Results:
  617.  * A standard Tcl result.
  618.  *
  619.  * Side effects:
  620.  * Creates and deletes interpreters.
  621.  *
  622.  *----------------------------------------------------------------------
  623.  */
  624. /* ARGSUSED */
  625. static int
  626. TestfeventCmd(clientData, interp, argc, argv)
  627.     ClientData clientData; /* Not used. */
  628.     Tcl_Interp *interp; /* Current interpreter. */
  629.     int argc; /* Number of arguments. */
  630.     CONST char **argv; /* Argument strings. */
  631. {
  632.     static Tcl_Interp *interp2 = NULL;
  633.     int code;
  634.     Tcl_Channel chan;
  635.     if (argc < 2) {
  636. Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  637. " option ?arg arg ...?", (char *) NULL);
  638. return TCL_ERROR;
  639.     }
  640.     if (strcmp(argv[1], "cmd") == 0) {
  641. if (argc != 3) {
  642.     Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  643.     " cmd script", (char *) NULL);
  644.     return TCL_ERROR;
  645. }
  646.         if (interp2 != (Tcl_Interp *) NULL) {
  647.             code = Tcl_GlobalEval(interp2, argv[2]);
  648.     Tcl_SetObjResult(interp, Tcl_GetObjResult(interp2));
  649.             return code;
  650.         } else {
  651.             Tcl_AppendResult(interp,
  652.                     "called "testfevent code" before "testfevent create"",
  653.                     (char *) NULL);
  654.             return TCL_ERROR;
  655.         }
  656.     } else if (strcmp(argv[1], "create") == 0) {
  657. if (interp2 != NULL) {
  658.             Tcl_DeleteInterp(interp2);
  659. }
  660.         interp2 = Tcl_CreateInterp();
  661. return Tcl_Init(interp2);
  662.     } else if (strcmp(argv[1], "delete") == 0) {
  663. if (interp2 != NULL) {
  664.             Tcl_DeleteInterp(interp2);
  665. }
  666. interp2 = NULL;
  667.     } else if (strcmp(argv[1], "share") == 0) {
  668.         if (interp2 != NULL) {
  669.             chan = Tcl_GetChannel(interp, argv[2], NULL);
  670.             if (chan == (Tcl_Channel) NULL) {
  671.                 return TCL_ERROR;
  672.             }
  673.             Tcl_RegisterChannel(interp2, chan);
  674.         }
  675.     }
  676.     
  677.     return TCL_OK;
  678. }
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * TestpanicCmd --
  683.  *
  684.  * Calls the panic routine.
  685.  *
  686.  * Results:
  687.  *      Always returns TCL_OK. 
  688.  *
  689.  * Side effects:
  690.  * May exit application.
  691.  *
  692.  *----------------------------------------------------------------------
  693.  */
  694. static int
  695. TestpanicCmd(dummy, interp, argc, argv)
  696.     ClientData dummy; /* Not used. */
  697.     Tcl_Interp *interp; /* Current interpreter. */
  698.     int argc; /* Number of arguments. */
  699.     CONST char **argv; /* Argument strings. */
  700. {
  701.     CONST char *argString;
  702.     
  703.     /*
  704.      *  Put the arguments into a var args structure
  705.      *  Append all of the arguments together separated by spaces
  706.      */
  707.     argString = Tcl_Merge(argc-1, argv+1);
  708.     panic(argString);
  709.     ckfree((char *)argString);
  710.  
  711.     return TCL_OK;
  712. }
  713. static int
  714. TestfileCmd(dummy, interp, argc, argv)
  715.     ClientData dummy; /* Not used. */
  716.     Tcl_Interp *interp; /* Current interpreter. */
  717.     int argc; /* Number of arguments. */
  718.     Tcl_Obj *CONST argv[]; /* The argument objects. */
  719. {
  720.     int force, i, j, result;
  721.     Tcl_Obj *error = NULL;
  722.     char *subcmd;
  723.     
  724.     if (argc < 3) {
  725. return TCL_ERROR;
  726.     }
  727.     force = 0;
  728.     i = 2;
  729.     if (strcmp(Tcl_GetString(argv[2]), "-force") == 0) {
  730.         force = 1;
  731. i = 3;
  732.     }
  733.     if (argc - i > 2) {
  734. return TCL_ERROR;
  735.     }
  736.     for (j = i; j < argc; j++) {
  737.         if (Tcl_FSGetNormalizedPath(interp, argv[j]) == NULL) {
  738.     return TCL_ERROR;
  739. }
  740.     }
  741.     subcmd = Tcl_GetString(argv[1]);
  742.     
  743.     if (strcmp(subcmd, "mv") == 0) {
  744. result = TclpObjRenameFile(argv[i], argv[i + 1]);
  745.     } else if (strcmp(subcmd, "cp") == 0) {
  746.         result = TclpObjCopyFile(argv[i], argv[i + 1]);
  747.     } else if (strcmp(subcmd, "rm") == 0) {
  748.         result = TclpObjDeleteFile(argv[i]);
  749.     } else if (strcmp(subcmd, "mkdir") == 0) {
  750.         result = TclpObjCreateDirectory(argv[i]);
  751.     } else if (strcmp(subcmd, "cpdir") == 0) {
  752.         result = TclpObjCopyDirectory(argv[i], argv[i + 1], &error);
  753.     } else if (strcmp(subcmd, "rmdir") == 0) {
  754.         result = TclpObjRemoveDirectory(argv[i], force, &error);
  755.     } else {
  756.         result = TCL_ERROR;
  757. goto end;
  758.     }
  759.     if (result != TCL_OK) {
  760. if (error != NULL) {
  761.     if (Tcl_GetString(error)[0] != '') {
  762. Tcl_AppendResult(interp, Tcl_GetString(error), " ", NULL);
  763.     }
  764.     Tcl_DecrRefCount(error);
  765. }
  766. Tcl_AppendResult(interp, Tcl_ErrnoId(), (char *) NULL);
  767.     }
  768.     end:
  769.     return result;
  770. }
  771. /*
  772.  *----------------------------------------------------------------------
  773.  *
  774.  * TestgetvarfullnameCmd --
  775.  *
  776.  * Implements the "testgetvarfullname" cmd that is used when testing
  777.  * the Tcl_GetVariableFullName procedure.
  778.  *
  779.  * Results:
  780.  * A standard Tcl result.
  781.  *
  782.  * Side effects:
  783.  * None.
  784.  *
  785.  *----------------------------------------------------------------------
  786.  */
  787. static int
  788. TestgetvarfullnameCmd(dummy, interp, objc, objv)
  789.     ClientData dummy; /* Not used. */
  790.     Tcl_Interp *interp; /* Current interpreter. */
  791.     int objc; /* Number of arguments. */
  792.     Tcl_Obj *CONST objv[]; /* The argument objects. */
  793. {
  794.     char *name, *arg;
  795.     int flags = 0;
  796.     Tcl_Namespace *namespacePtr;
  797.     Tcl_CallFrame frame;
  798.     Tcl_Var variable;
  799.     int result;
  800.     if (objc != 3) {
  801. Tcl_WrongNumArgs(interp, 1, objv, "name scope");
  802.         return TCL_ERROR;
  803.     }
  804.     
  805.     name = Tcl_GetString(objv[1]);
  806.     arg = Tcl_GetString(objv[2]);
  807.     if (strcmp(arg, "global") == 0) {
  808. flags = TCL_GLOBAL_ONLY;
  809.     } else if (strcmp(arg, "namespace") == 0) {
  810. flags = TCL_NAMESPACE_ONLY;
  811.     }
  812.     /*
  813.      * This command, like any other created with Tcl_Create[Obj]Command,
  814.      * runs in the global namespace. As a "namespace-aware" command that
  815.      * needs to run in a particular namespace, it must activate that
  816.      * namespace itself.
  817.      */
  818.     if (flags == TCL_NAMESPACE_ONLY) {
  819. namespacePtr = Tcl_FindNamespace(interp, "::test_ns_var",
  820.         (Tcl_Namespace *) NULL, TCL_LEAVE_ERR_MSG);
  821. if (namespacePtr == NULL) {
  822.     return TCL_ERROR;
  823. }
  824. result = Tcl_PushCallFrame(interp, &frame, namespacePtr,
  825.                 /*isProcCallFrame*/ 0);
  826. if (result != TCL_OK) {
  827.     return result;
  828. }
  829.     }
  830.     
  831.     variable = Tcl_FindNamespaceVar(interp, name, (Tcl_Namespace *) NULL,
  832.     (flags | TCL_LEAVE_ERR_MSG));
  833.     if (flags == TCL_NAMESPACE_ONLY) {
  834. Tcl_PopCallFrame(interp);
  835.     }
  836.     if (variable == (Tcl_Var) NULL) {
  837. return TCL_ERROR;
  838.     }
  839.     Tcl_GetVariableFullName(interp, variable, Tcl_GetObjResult(interp));
  840.     return TCL_OK;
  841. }
  842. /*
  843.  *----------------------------------------------------------------------
  844.  *
  845.  * GetTimesCmd --
  846.  *
  847.  * This procedure implements the "gettimes" command.  It is
  848.  * used for computing the time needed for various basic operations
  849.  * such as reading variables, allocating memory, sprintf, converting
  850.  * variables, etc.
  851.  *
  852.  * Results:
  853.  * A standard Tcl result.
  854.  *
  855.  * Side effects:
  856.  * Allocates and frees memory, sets a variable "a" in the interpreter.
  857.  *
  858.  *----------------------------------------------------------------------
  859.  */
  860. static int
  861. GetTimesCmd(unused, interp, argc, argv)
  862.     ClientData unused; /* Unused. */
  863.     Tcl_Interp *interp; /* The current interpreter. */
  864.     int argc; /* The number of arguments. */
  865.     CONST char **argv; /* The argument strings. */
  866. {
  867.     Interp *iPtr = (Interp *) interp;
  868.     int i, n;
  869.     double timePer;
  870.     Tcl_Time start, stop;
  871.     Tcl_Obj *objPtr;
  872.     Tcl_Obj **objv;
  873.     CONST char *s;
  874.     char newString[TCL_INTEGER_SPACE];
  875.     /* alloc & free 100000 times */
  876.     fprintf(stderr, "alloc & free 100000 6 word itemsn");
  877.     Tcl_GetTime(&start);
  878.     for (i = 0;  i < 100000;  i++) {
  879. objPtr = (Tcl_Obj *) ckalloc(sizeof(Tcl_Obj));
  880. ckfree((char *) objPtr);
  881.     }
  882.     Tcl_GetTime(&stop);
  883.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  884.     fprintf(stderr, "   %.3f usec per alloc+freen", timePer/100000);
  885.     
  886.     /* alloc 5000 times */
  887.     fprintf(stderr, "alloc 5000 6 word itemsn");
  888.     objv = (Tcl_Obj **) ckalloc(5000 * sizeof(Tcl_Obj *));
  889.     Tcl_GetTime(&start);
  890.     for (i = 0;  i < 5000;  i++) {
  891. objv[i] = (Tcl_Obj *) ckalloc(sizeof(Tcl_Obj));
  892.     }
  893.     Tcl_GetTime(&stop);
  894.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  895.     fprintf(stderr, "   %.3f usec per allocn", timePer/5000);
  896.     
  897.     /* free 5000 times */
  898.     fprintf(stderr, "free 5000 6 word itemsn");
  899.     Tcl_GetTime(&start);
  900.     for (i = 0;  i < 5000;  i++) {
  901. ckfree((char *) objv[i]);
  902.     }
  903.     Tcl_GetTime(&stop);
  904.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  905.     fprintf(stderr, "   %.3f usec per freen", timePer/5000);
  906.     /* Tcl_NewObj 5000 times */
  907.     fprintf(stderr, "Tcl_NewObj 5000 timesn");
  908.     Tcl_GetTime(&start);
  909.     for (i = 0;  i < 5000;  i++) {
  910. objv[i] = Tcl_NewObj();
  911.     }
  912.     Tcl_GetTime(&stop);
  913.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  914.     fprintf(stderr, "   %.3f usec per Tcl_NewObjn", timePer/5000);
  915.     
  916.     /* Tcl_DecrRefCount 5000 times */
  917.     fprintf(stderr, "Tcl_DecrRefCount 5000 timesn");
  918.     Tcl_GetTime(&start);
  919.     for (i = 0;  i < 5000;  i++) {
  920. objPtr = objv[i];
  921. Tcl_DecrRefCount(objPtr);
  922.     }
  923.     Tcl_GetTime(&stop);
  924.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  925.     fprintf(stderr, "   %.3f usec per Tcl_DecrRefCountn", timePer/5000);
  926.     ckfree((char *) objv);
  927.     /* TclGetString 100000 times */
  928.     fprintf(stderr, "TclGetStringFromObj of "12345" 100000 timesn");
  929.     objPtr = Tcl_NewStringObj("12345", -1);
  930.     Tcl_GetTime(&start);
  931.     for (i = 0;  i < 100000;  i++) {
  932. (void) TclGetString(objPtr);
  933.     }
  934.     Tcl_GetTime(&stop);
  935.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  936.     fprintf(stderr, "   %.3f usec per TclGetStringFromObj of "12345"n",
  937.     timePer/100000);
  938.     /* Tcl_GetIntFromObj 100000 times */
  939.     fprintf(stderr, "Tcl_GetIntFromObj of "12345" 100000 timesn");
  940.     Tcl_GetTime(&start);
  941.     for (i = 0;  i < 100000;  i++) {
  942. if (Tcl_GetIntFromObj(interp, objPtr, &n) != TCL_OK) {
  943.     return TCL_ERROR;
  944. }
  945.     }
  946.     Tcl_GetTime(&stop);
  947.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  948.     fprintf(stderr, "   %.3f usec per Tcl_GetIntFromObj of "12345"n",
  949.     timePer/100000);
  950.     Tcl_DecrRefCount(objPtr);
  951.     
  952.     /* Tcl_GetInt 100000 times */
  953.     fprintf(stderr, "Tcl_GetInt of "12345" 100000 timesn");
  954.     Tcl_GetTime(&start);
  955.     for (i = 0;  i < 100000;  i++) {
  956. if (Tcl_GetInt(interp, "12345", &n) != TCL_OK) {
  957.     return TCL_ERROR;
  958. }
  959.     }
  960.     Tcl_GetTime(&stop);
  961.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  962.     fprintf(stderr, "   %.3f usec per Tcl_GetInt of "12345"n",
  963.     timePer/100000);
  964.     /* sprintf 100000 times */
  965.     fprintf(stderr, "sprintf of 12345 100000 timesn");
  966.     Tcl_GetTime(&start);
  967.     for (i = 0;  i < 100000;  i++) {
  968. sprintf(newString, "%d", 12345);
  969.     }
  970.     Tcl_GetTime(&stop);
  971.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  972.     fprintf(stderr, "   %.3f usec per sprintf of 12345n",
  973.     timePer/100000);
  974.     /* hashtable lookup 100000 times */
  975.     fprintf(stderr, "hashtable lookup of "gettimes" 100000 timesn");
  976.     Tcl_GetTime(&start);
  977.     for (i = 0;  i < 100000;  i++) {
  978. (void) Tcl_FindHashEntry(&iPtr->globalNsPtr->cmdTable, "gettimes");
  979.     }
  980.     Tcl_GetTime(&stop);
  981.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  982.     fprintf(stderr, "   %.3f usec per hashtable lookup of "gettimes"n",
  983.     timePer/100000);
  984.     /* Tcl_SetVar 100000 times */
  985.     fprintf(stderr, "Tcl_SetVar of "12345" 100000 timesn");
  986.     Tcl_GetTime(&start);
  987.     for (i = 0;  i < 100000;  i++) {
  988. s = Tcl_SetVar(interp, "a", "12345", TCL_LEAVE_ERR_MSG);
  989. if (s == NULL) {
  990.     return TCL_ERROR;
  991. }
  992.     }
  993.     Tcl_GetTime(&stop);
  994.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  995.     fprintf(stderr, "   %.3f usec per Tcl_SetVar of a to "12345"n",
  996.     timePer/100000);
  997.     /* Tcl_GetVar 100000 times */
  998.     fprintf(stderr, "Tcl_GetVar of a=="12345" 100000 timesn");
  999.     Tcl_GetTime(&start);
  1000.     for (i = 0;  i < 100000;  i++) {
  1001. s = Tcl_GetVar(interp, "a", TCL_LEAVE_ERR_MSG);
  1002. if (s == NULL) {
  1003.     return TCL_ERROR;
  1004. }
  1005.     }
  1006.     Tcl_GetTime(&stop);
  1007.     timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
  1008.     fprintf(stderr, "   %.3f usec per Tcl_GetVar of a=="12345"n",
  1009.     timePer/100000);
  1010.     
  1011.     Tcl_ResetResult(interp);
  1012.     return TCL_OK;
  1013. }
  1014. /*
  1015.  *----------------------------------------------------------------------
  1016.  *
  1017.  * NoopCmd --
  1018.  *
  1019.  * This procedure is just used to time the overhead involved in
  1020.  * parsing and invoking a command.
  1021.  *
  1022.  * Results:
  1023.  * None.
  1024.  *
  1025.  * Side effects:
  1026.  * None.
  1027.  *
  1028.  *----------------------------------------------------------------------
  1029.  */
  1030. static int
  1031. NoopCmd(unused, interp, argc, argv)
  1032.     ClientData unused; /* Unused. */
  1033.     Tcl_Interp *interp; /* The current interpreter. */
  1034.     int argc; /* The number of arguments. */
  1035.     CONST char **argv; /* The argument strings. */
  1036. {
  1037.     return TCL_OK;
  1038. }
  1039. /*
  1040.  *----------------------------------------------------------------------
  1041.  *
  1042.  * NoopObjCmd --
  1043.  *
  1044.  * This object-based procedure is just used to time the overhead
  1045.  * involved in parsing and invoking a command.
  1046.  *
  1047.  * Results:
  1048.  * Returns the TCL_OK result code.
  1049.  *
  1050.  * Side effects:
  1051.  * None.
  1052.  *
  1053.  *----------------------------------------------------------------------
  1054.  */
  1055. static int
  1056. NoopObjCmd(unused, interp, objc, objv)
  1057.     ClientData unused; /* Not used. */
  1058.     Tcl_Interp *interp; /* Current interpreter. */
  1059.     int objc; /* Number of arguments. */
  1060.     Tcl_Obj *CONST objv[]; /* The argument objects. */
  1061. {
  1062.     return TCL_OK;
  1063. }
  1064. /*
  1065.  *----------------------------------------------------------------------
  1066.  *
  1067.  * TestsetCmd --
  1068.  *
  1069.  * Implements the "testset{err,noerr}" cmds that are used when testing
  1070.  * Tcl_Set/GetVar C Api with/without TCL_LEAVE_ERR_MSG flag
  1071.  *
  1072.  * Results:
  1073.  * A standard Tcl result.
  1074.  *
  1075.  * Side effects:
  1076.  *     Variables may be set.
  1077.  *
  1078.  *----------------------------------------------------------------------
  1079.  */
  1080. /* ARGSUSED */
  1081. static int
  1082. TestsetCmd(data, interp, argc, argv)
  1083.     ClientData data; /* Additional flags for Get/SetVar2. */
  1084.     register Tcl_Interp *interp; /* Current interpreter. */
  1085.     int argc; /* Number of arguments. */
  1086.     CONST char **argv; /* Argument strings. */
  1087. {
  1088.     int flags = (int) data;
  1089.     CONST char *value;
  1090.     if (argc == 2) {
  1091.         Tcl_SetResult(interp, "before get", TCL_STATIC);
  1092. value = Tcl_GetVar2(interp, argv[1], (char *) NULL, flags);
  1093.         if (value == NULL) {
  1094.             return TCL_ERROR;
  1095.         }
  1096. Tcl_AppendElement(interp, value);
  1097.         return TCL_OK;
  1098.     } else if (argc == 3) {
  1099. Tcl_SetResult(interp, "before set", TCL_STATIC);
  1100.         value = Tcl_SetVar2(interp, argv[1], (char *) NULL, argv[2], flags);
  1101.         if (value == NULL) {
  1102.             return TCL_ERROR;
  1103.         }
  1104. Tcl_AppendElement(interp, value);
  1105. return TCL_OK;
  1106.     } else {
  1107. Tcl_AppendResult(interp, "wrong # args: should be "",
  1108. argv[0], " varName ?newValue?"", (char *) NULL);
  1109. return TCL_ERROR;
  1110.     }
  1111. }
  1112. /*
  1113.  *----------------------------------------------------------------------
  1114.  *
  1115.  * TestsaveresultCmd --
  1116.  *
  1117.  * Implements the "testsaveresult" cmd that is used when testing
  1118.  * the Tcl_SaveResult, Tcl_RestoreResult, and
  1119.  * Tcl_DiscardResult interfaces.
  1120.  *
  1121.  * Results:
  1122.  * A standard Tcl result.
  1123.  *
  1124.  * Side effects:
  1125.  * None.
  1126.  *
  1127.  *----------------------------------------------------------------------
  1128.  */
  1129. /* ARGSUSED */
  1130. static int
  1131. TestsaveresultCmd(dummy, interp, objc, objv)
  1132.     ClientData dummy; /* Not used. */
  1133.     register Tcl_Interp *interp; /* Current interpreter. */
  1134.     int objc; /* Number of arguments. */
  1135.     Tcl_Obj *CONST objv[]; /* The argument objects. */
  1136. {
  1137.     int discard, result, index;
  1138.     Tcl_SavedResult state;
  1139.     Tcl_Obj *objPtr;
  1140.     static CONST char *optionStrings[] = {
  1141. "append", "dynamic", "free", "object", "small", NULL
  1142.     };
  1143.     enum options {
  1144. RESULT_APPEND, RESULT_DYNAMIC, RESULT_FREE, RESULT_OBJECT, RESULT_SMALL
  1145.     };
  1146.     /*
  1147.      * Parse arguments
  1148.      */
  1149.     if (objc != 4) {
  1150. Tcl_WrongNumArgs(interp, 1, objv, "type script discard");
  1151.         return TCL_ERROR;
  1152.     }
  1153.     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
  1154.     &index) != TCL_OK) {
  1155. return TCL_ERROR;
  1156.     }
  1157.     if (Tcl_GetBooleanFromObj(interp, objv[3], &discard) != TCL_OK) {
  1158. return TCL_ERROR;
  1159.     }
  1160.     objPtr = NULL; /* Lint. */
  1161.     switch ((enum options) index) {
  1162. case RESULT_SMALL:
  1163.     Tcl_SetResult(interp, "small result", TCL_VOLATILE);
  1164.     break;
  1165. case RESULT_APPEND:
  1166.     Tcl_AppendResult(interp, "append result", NULL);
  1167.     break;
  1168. case RESULT_FREE: {
  1169.     char *buf = ckalloc(200);
  1170.     strcpy(buf, "free result");
  1171.     Tcl_SetResult(interp, buf, TCL_DYNAMIC);
  1172.     break;
  1173. }
  1174. case RESULT_DYNAMIC:
  1175.     Tcl_SetResult(interp, "dynamic result", TestsaveresultFree);
  1176.     break;
  1177. case RESULT_OBJECT:
  1178.     objPtr = Tcl_NewStringObj("object result", -1);
  1179.     Tcl_SetObjResult(interp, objPtr);
  1180.     break;
  1181.     }
  1182.     freeCount = 0;
  1183.     Tcl_SaveResult(interp, &state);
  1184.     if (((enum options) index) == RESULT_OBJECT) {
  1185. result = Tcl_EvalObjEx(interp, objv[2], 0);
  1186.     } else {
  1187. result = Tcl_Eval(interp, Tcl_GetString(objv[2]));
  1188.     }
  1189.     if (discard) {
  1190. Tcl_DiscardResult(&state);
  1191.     } else {
  1192. Tcl_RestoreResult(interp, &state);
  1193. result = TCL_OK;
  1194.     }
  1195.     switch ((enum options) index) {
  1196. case RESULT_DYNAMIC: {
  1197.     int present = interp->freeProc == TestsaveresultFree;
  1198.     int called = freeCount;
  1199.     Tcl_AppendElement(interp, called ? "called" : "notCalled");
  1200.     Tcl_AppendElement(interp, present ? "present" : "missing");
  1201.     break;
  1202. }
  1203. case RESULT_OBJECT:
  1204.     Tcl_AppendElement(interp, Tcl_GetObjResult(interp) == objPtr
  1205.     ? "same" : "different");
  1206.     break;
  1207. default:
  1208.     break;
  1209.     }
  1210.     return result;
  1211. }
  1212. /*
  1213.  *----------------------------------------------------------------------
  1214.  *
  1215.  * TestsaveresultFree --
  1216.  *
  1217.  * Special purpose freeProc used by TestsaveresultCmd.
  1218.  *
  1219.  * Results:
  1220.  * None.
  1221.  *
  1222.  * Side effects:
  1223.  * Increments the freeCount.
  1224.  *
  1225.  *----------------------------------------------------------------------
  1226.  */
  1227. static void
  1228. TestsaveresultFree(blockPtr)
  1229.     char *blockPtr;
  1230. {
  1231.     freeCount++;
  1232. }
  1233. /*
  1234.  *----------------------------------------------------------------------
  1235.  *
  1236.  * TeststatprocCmd  --
  1237.  *
  1238.  * Implements the "testTclStatProc" cmd that is used to test the
  1239.  * 'TclStatInsertProc' & 'TclStatDeleteProc' C Apis.
  1240.  *
  1241.  * Results:
  1242.  * A standard Tcl result.
  1243.  *
  1244.  * Side effects:
  1245.  * None.
  1246.  *
  1247.  *----------------------------------------------------------------------
  1248.  */
  1249. static int
  1250. TeststatprocCmd (dummy, interp, argc, argv)
  1251.     ClientData dummy; /* Not used. */
  1252.     register Tcl_Interp *interp; /* Current interpreter. */
  1253.     int argc; /* Number of arguments. */
  1254.     CONST char **argv; /* Argument strings. */
  1255. {
  1256.     TclStatProc_ *proc;
  1257.     int retVal;
  1258.     if (argc != 3) {
  1259. Tcl_AppendResult(interp, "wrong # args: should be "",
  1260. argv[0], " option arg"", (char *) NULL);
  1261. return TCL_ERROR;
  1262.     }
  1263.     if (strcmp(argv[2], "TclpStat") == 0) {
  1264. proc = PretendTclpStat;
  1265.     } else if (strcmp(argv[2], "TestStatProc1") == 0) {
  1266. proc = TestStatProc1;
  1267.     } else if (strcmp(argv[2], "TestStatProc2") == 0) {
  1268. proc = TestStatProc2;
  1269.     } else if (strcmp(argv[2], "TestStatProc3") == 0) {
  1270. proc = TestStatProc3;
  1271.     } else {
  1272. Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1273. "must be TclpStat, ",
  1274. "TestStatProc1, TestStatProc2, or TestStatProc3",
  1275. (char *) NULL);
  1276. return TCL_ERROR;
  1277.     }
  1278.     if (strcmp(argv[1], "insert") == 0) {
  1279. if (proc == PretendTclpStat) {
  1280.     Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1281.    "must be ",
  1282.    "TestStatProc1, TestStatProc2, or TestStatProc3",
  1283.    (char *) NULL);
  1284.     return TCL_ERROR;
  1285. }
  1286. retVal = TclStatInsertProc(proc);
  1287.     } else if (strcmp(argv[1], "delete") == 0) {
  1288. retVal = TclStatDeleteProc(proc);
  1289.     } else {
  1290. Tcl_AppendResult(interp, "bad option "", argv[1], "": ",
  1291. "must be insert or delete", (char *) NULL);
  1292. return TCL_ERROR;
  1293.     }
  1294.     if (retVal == TCL_ERROR) {
  1295. Tcl_AppendResult(interp, """, argv[2], "": ",
  1296. "could not be ", argv[1], "ed", (char *) NULL);
  1297.     }
  1298.     return retVal;
  1299. }
  1300. static int PretendTclpStat(path, buf)
  1301.     CONST char *path;
  1302.     struct stat *buf;
  1303. {
  1304.     int ret;
  1305.     Tcl_Obj *pathPtr = Tcl_NewStringObj(path, -1);
  1306. #ifdef TCL_WIDE_INT_IS_LONG
  1307.     Tcl_IncrRefCount(pathPtr);
  1308.     ret = TclpObjStat(pathPtr, buf);
  1309.     Tcl_DecrRefCount(pathPtr);
  1310.     return ret;
  1311. #else /* TCL_WIDE_INT_IS_LONG */
  1312.     Tcl_StatBuf realBuf;
  1313.     Tcl_IncrRefCount(pathPtr);
  1314.     ret = TclpObjStat(pathPtr, &realBuf);
  1315.     Tcl_DecrRefCount(pathPtr);
  1316.     if (ret != -1) {
  1317. #   define OUT_OF_RANGE(x) 
  1318. (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || 
  1319.  ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX))
  1320. #if defined(__GNUC__) && __GNUC__ >= 2
  1321. /*
  1322.  * Workaround gcc warning of "comparison is always false due to limited range of
  1323.  * data type" in this macro by checking max type size, and when necessary ANDing
  1324.  * with the complement of ULONG_MAX instead of the comparison:
  1325.  */
  1326. #   define OUT_OF_URANGE(x) 
  1327. ((((Tcl_WideUInt)(~ (__typeof__(x)) 0)) > (Tcl_WideUInt)ULONG_MAX) && 
  1328.  (((Tcl_WideUInt)(x)) & ~(Tcl_WideUInt)ULONG_MAX))
  1329. #else
  1330. #   define OUT_OF_URANGE(x) 
  1331. (((Tcl_WideUInt)(x)) > (Tcl_WideUInt)ULONG_MAX)
  1332. #endif
  1333. /*
  1334.  * Perform the result-buffer overflow check manually.
  1335.  *
  1336.  * Note that ino_t/ino64_t is unsigned...
  1337.  */
  1338.         if (OUT_OF_URANGE(realBuf.st_ino) || OUT_OF_RANGE(realBuf.st_size)
  1339. #   ifdef HAVE_ST_BLOCKS
  1340. || OUT_OF_RANGE(realBuf.st_blocks)
  1341. #   endif
  1342.     ) {
  1343. #   ifdef EOVERFLOW
  1344.     errno = EOVERFLOW;
  1345. #   else
  1346. #       ifdef EFBIG
  1347.             errno = EFBIG;
  1348. #       else
  1349. #           error "what error should be returned for a value out of range?"
  1350. #       endif
  1351. #   endif
  1352.     return -1;
  1353. }
  1354. #   undef OUT_OF_RANGE
  1355. #   undef OUT_OF_URANGE
  1356. /*
  1357.  * Copy across all supported fields, with possible type
  1358.  * coercions on those fields that change between the normal
  1359.  * and lf64 versions of the stat structure (on Solaris at
  1360.  * least.)  This is slow when the structure sizes coincide,
  1361.  * but that's what you get for mixing interfaces...
  1362.  */
  1363. buf->st_mode    = realBuf.st_mode;
  1364. buf->st_ino     = (ino_t) realBuf.st_ino;
  1365. buf->st_dev     = realBuf.st_dev;
  1366. buf->st_rdev    = realBuf.st_rdev;
  1367. buf->st_nlink   = realBuf.st_nlink;
  1368. buf->st_uid     = realBuf.st_uid;
  1369. buf->st_gid     = realBuf.st_gid;
  1370. buf->st_size    = (off_t) realBuf.st_size;
  1371. buf->st_atime   = realBuf.st_atime;
  1372. buf->st_mtime   = realBuf.st_mtime;
  1373. buf->st_ctime   = realBuf.st_ctime;
  1374. #   ifdef HAVE_ST_BLOCKS
  1375. buf->st_blksize = realBuf.st_blksize;
  1376. buf->st_blocks  = (blkcnt_t) realBuf.st_blocks;
  1377. #   endif
  1378.     }
  1379.     return ret;
  1380. #endif /* TCL_WIDE_INT_IS_LONG */
  1381. }
  1382. /* Be careful in the compares in these tests, since the Macintosh puts a  
  1383.  * leading : in the beginning of non-absolute paths before passing them 
  1384.  * into the file command procedures.
  1385.  */
  1386. static int
  1387. TestStatProc1(path, buf)
  1388.     CONST char *path;
  1389.     struct stat *buf;
  1390. {
  1391.     memset(buf, 0, sizeof(struct stat));
  1392.     buf->st_size = 1234;
  1393.     return ((strstr(path, "testStat1%.fil") == NULL) ? -1 : 0);
  1394. }
  1395. static int
  1396. TestStatProc2(path, buf)
  1397.     CONST char *path;
  1398.     struct stat *buf;
  1399. {
  1400.     memset(buf, 0, sizeof(struct stat));
  1401.     buf->st_size = 2345;
  1402.     return ((strstr(path, "testStat2%.fil") == NULL) ? -1 : 0);
  1403. }
  1404. static int
  1405. TestStatProc3(path, buf)
  1406.     CONST char *path;
  1407.     struct stat *buf;
  1408. {
  1409.     memset(buf, 0, sizeof(struct stat));
  1410.     buf->st_size = 3456;
  1411.     return ((strstr(path, "testStat3%.fil") == NULL) ? -1 : 0);
  1412. }
  1413. /*
  1414.  *----------------------------------------------------------------------
  1415.  *
  1416.  * TestmainthreadCmd  --
  1417.  *
  1418.  * Implements the "testmainthread" cmd that is used to test the
  1419.  * 'Tcl_GetCurrentThread' API.
  1420.  *
  1421.  * Results:
  1422.  * A standard Tcl result.
  1423.  *
  1424.  * Side effects:
  1425.  * None.
  1426.  *
  1427.  *----------------------------------------------------------------------
  1428.  */
  1429. static int
  1430. TestmainthreadCmd (dummy, interp, argc, argv)
  1431.     ClientData dummy; /* Not used. */
  1432.     register Tcl_Interp *interp; /* Current interpreter. */
  1433.     int argc; /* Number of arguments. */
  1434.     CONST char **argv; /* Argument strings. */
  1435. {
  1436.   if (argc == 1) {
  1437.       Tcl_Obj *idObj = Tcl_NewLongObj((long)Tcl_GetCurrentThread());
  1438.       Tcl_SetObjResult(interp, idObj);
  1439.       return TCL_OK;
  1440.   } else {
  1441.       Tcl_SetResult(interp, "wrong # args", TCL_STATIC);
  1442.       return TCL_ERROR;
  1443.   }
  1444. }
  1445. /*
  1446.  *----------------------------------------------------------------------
  1447.  *
  1448.  * MainLoop --
  1449.  *
  1450.  * A main loop set by TestsetmainloopCmd below.
  1451.  *
  1452.  * Results:
  1453.  *  None.
  1454.  *
  1455.  * Side effects:
  1456.  * Event handlers could do anything.
  1457.  *
  1458.  *----------------------------------------------------------------------
  1459.  */
  1460. static void
  1461. MainLoop(void)
  1462. {
  1463.     while (!exitMainLoop) {
  1464. Tcl_DoOneEvent(0);
  1465.     }
  1466.     fprintf(stdout,"Exit MainLoopn");
  1467.     fflush(stdout);
  1468. }
  1469. /*
  1470.  *----------------------------------------------------------------------
  1471.  *
  1472.  * TestsetmainloopCmd  --
  1473.  *
  1474.  * Implements the "testsetmainloop" cmd that is used to test the
  1475.  * 'Tcl_SetMainLoop' API.
  1476.  *
  1477.  * Results:
  1478.  * A standard Tcl result.
  1479.  *
  1480.  * Side effects:
  1481.  * None.
  1482.  *
  1483.  *----------------------------------------------------------------------
  1484.  */
  1485. static int
  1486. TestsetmainloopCmd (dummy, interp, argc, argv)
  1487.     ClientData dummy; /* Not used. */
  1488.     register Tcl_Interp *interp; /* Current interpreter. */
  1489.     int argc; /* Number of arguments. */
  1490.     CONST char **argv; /* Argument strings. */
  1491. {
  1492.   exitMainLoop = 0;
  1493.   Tcl_SetMainLoop(MainLoop);
  1494.   return TCL_OK;
  1495. }
  1496. /*
  1497.  *----------------------------------------------------------------------
  1498.  *
  1499.  * TestexitmainloopCmd  --
  1500.  *
  1501.  * Implements the "testexitmainloop" cmd that is used to test the
  1502.  * 'Tcl_SetMainLoop' API.
  1503.  *
  1504.  * Results:
  1505.  * A standard Tcl result.
  1506.  *
  1507.  * Side effects:
  1508.  * None.
  1509.  *
  1510.  *----------------------------------------------------------------------
  1511.  */
  1512. static int
  1513. TestexitmainloopCmd (dummy, interp, argc, argv)
  1514.     ClientData dummy; /* Not used. */
  1515.     register Tcl_Interp *interp; /* Current interpreter. */
  1516.     int argc; /* Number of arguments. */
  1517.     CONST char **argv; /* Argument strings. */
  1518. {
  1519.   exitMainLoop = 1;
  1520.   return TCL_OK;
  1521. }
  1522. /*
  1523.  *----------------------------------------------------------------------
  1524.  *
  1525.  * TestaccessprocCmd  --
  1526.  *
  1527.  * Implements the "testTclAccessProc" cmd that is used to test the
  1528.  * 'TclAccessInsertProc' & 'TclAccessDeleteProc' C Apis.
  1529.  *
  1530.  * Results:
  1531.  * A standard Tcl result.
  1532.  *
  1533.  * Side effects:
  1534.  * None.
  1535.  *
  1536.  *----------------------------------------------------------------------
  1537.  */
  1538. static int
  1539. TestaccessprocCmd (dummy, interp, argc, argv)
  1540.     ClientData dummy; /* Not used. */
  1541.     register Tcl_Interp *interp; /* Current interpreter. */
  1542.     int argc; /* Number of arguments. */
  1543.     CONST char **argv; /* Argument strings. */
  1544. {
  1545.     TclAccessProc_ *proc;
  1546.     int retVal;
  1547.     if (argc != 3) {
  1548. Tcl_AppendResult(interp, "wrong # args: should be "",
  1549. argv[0], " option arg"", (char *) NULL);
  1550. return TCL_ERROR;
  1551.     }
  1552.     if (strcmp(argv[2], "TclpAccess") == 0) {
  1553. proc = PretendTclpAccess;
  1554.     } else if (strcmp(argv[2], "TestAccessProc1") == 0) {
  1555. proc = TestAccessProc1;
  1556.     } else if (strcmp(argv[2], "TestAccessProc2") == 0) {
  1557. proc = TestAccessProc2;
  1558.     } else if (strcmp(argv[2], "TestAccessProc3") == 0) {
  1559. proc = TestAccessProc3;
  1560.     } else {
  1561. Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1562. "must be TclpAccess, ",
  1563. "TestAccessProc1, TestAccessProc2, or TestAccessProc3",
  1564. (char *) NULL);
  1565. return TCL_ERROR;
  1566.     }
  1567.     if (strcmp(argv[1], "insert") == 0) {
  1568. if (proc == PretendTclpAccess) {
  1569.     Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1570.    "must be ",
  1571.    "TestAccessProc1, TestAccessProc2, or TestAccessProc3",
  1572.    (char *) NULL);
  1573.     return TCL_ERROR;
  1574. }
  1575. retVal = TclAccessInsertProc(proc);
  1576.     } else if (strcmp(argv[1], "delete") == 0) {
  1577. retVal = TclAccessDeleteProc(proc);
  1578.     } else {
  1579. Tcl_AppendResult(interp, "bad option "", argv[1], "": ",
  1580. "must be insert or delete", (char *) NULL);
  1581. return TCL_ERROR;
  1582.     }
  1583.     if (retVal == TCL_ERROR) {
  1584. Tcl_AppendResult(interp, """, argv[2], "": ",
  1585. "could not be ", argv[1], "ed", (char *) NULL);
  1586.     }
  1587.     return retVal;
  1588. }
  1589. static int PretendTclpAccess(path, mode)
  1590.     CONST char *path;
  1591.     int mode;
  1592. {
  1593.     int ret;
  1594.     Tcl_Obj *pathPtr = Tcl_NewStringObj(path, -1);
  1595.     Tcl_IncrRefCount(pathPtr);
  1596.     ret = TclpObjAccess(pathPtr, mode);
  1597.     Tcl_DecrRefCount(pathPtr);
  1598.     return ret;
  1599. }
  1600. static int
  1601. TestAccessProc1(path, mode)
  1602.     CONST char *path;
  1603.     int mode;
  1604. {
  1605.     return ((strstr(path, "testAccess1%.fil") == NULL) ? -1 : 0);
  1606. }
  1607. static int
  1608. TestAccessProc2(path, mode)
  1609.     CONST char *path;
  1610.     int mode;
  1611. {
  1612.     return ((strstr(path, "testAccess2%.fil") == NULL) ? -1 : 0);
  1613. }
  1614. static int
  1615. TestAccessProc3(path, mode)
  1616.     CONST char *path;
  1617.     int mode;
  1618. {
  1619.     return ((strstr(path, "testAccess3%.fil") == NULL) ? -1 : 0);
  1620. }
  1621. /*
  1622.  *----------------------------------------------------------------------
  1623.  *
  1624.  * TestopenfilechannelprocCmd  --
  1625.  *
  1626.  * Implements the "testTclOpenFileChannelProc" cmd that is used to test the
  1627.  * 'TclOpenFileChannelInsertProc' & 'TclOpenFileChannelDeleteProc' C Apis.
  1628.  *
  1629.  * Results:
  1630.  * A standard Tcl result.
  1631.  *
  1632.  * Side effects:
  1633.  * None.
  1634.  *
  1635.  *----------------------------------------------------------------------
  1636.  */
  1637. static int
  1638. TestopenfilechannelprocCmd (dummy, interp, argc, argv)
  1639.     ClientData dummy; /* Not used. */
  1640.     register Tcl_Interp *interp; /* Current interpreter. */
  1641.     int argc; /* Number of arguments. */
  1642.     CONST char **argv; /* Argument strings. */
  1643. {
  1644.     TclOpenFileChannelProc_ *proc;
  1645.     int retVal;
  1646.     if (argc != 3) {
  1647. Tcl_AppendResult(interp, "wrong # args: should be "",
  1648. argv[0], " option arg"", (char *) NULL);
  1649. return TCL_ERROR;
  1650.     }
  1651.     if (strcmp(argv[2], "TclpOpenFileChannel") == 0) {
  1652. proc = PretendTclpOpenFileChannel;
  1653.     } else if (strcmp(argv[2], "TestOpenFileChannelProc1") == 0) {
  1654. proc = TestOpenFileChannelProc1;
  1655.     } else if (strcmp(argv[2], "TestOpenFileChannelProc2") == 0) {
  1656. proc = TestOpenFileChannelProc2;
  1657.     } else if (strcmp(argv[2], "TestOpenFileChannelProc3") == 0) {
  1658. proc = TestOpenFileChannelProc3;
  1659.     } else {
  1660. Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1661. "must be TclpOpenFileChannel, ",
  1662. "TestOpenFileChannelProc1, TestOpenFileChannelProc2, or ",
  1663. "TestOpenFileChannelProc3",
  1664. (char *) NULL);
  1665. return TCL_ERROR;
  1666.     }
  1667.     if (strcmp(argv[1], "insert") == 0) {
  1668. if (proc == PretendTclpOpenFileChannel) {
  1669.     Tcl_AppendResult(interp, "bad arg "", argv[1], "": ",
  1670.    "must be ",
  1671.    "TestOpenFileChannelProc1, TestOpenFileChannelProc2, or ",
  1672.    "TestOpenFileChannelProc3",
  1673.    (char *) NULL);
  1674.     return TCL_ERROR;
  1675. }
  1676. retVal = TclOpenFileChannelInsertProc(proc);
  1677.     } else if (strcmp(argv[1], "delete") == 0) {
  1678. retVal = TclOpenFileChannelDeleteProc(proc);
  1679.     } else {
  1680. Tcl_AppendResult(interp, "bad option "", argv[1], "": ",
  1681. "must be insert or delete", (char *) NULL);
  1682. return TCL_ERROR;
  1683.     }
  1684.     if (retVal == TCL_ERROR) {
  1685. Tcl_AppendResult(interp, """, argv[2], "": ",
  1686. "could not be ", argv[1], "ed", (char *) NULL);
  1687.     }
  1688.     return retVal;
  1689. }
  1690. static Tcl_Channel
  1691. PretendTclpOpenFileChannel(interp, fileName, modeString, permissions)
  1692.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  1693.  * can be NULL. */
  1694.     CONST char *fileName;               /* Name of file to open. */
  1695.     CONST char *modeString;             /* A list of POSIX open modes or
  1696.  * a string such as "rw". */
  1697.     int permissions;                    /* If the open involves creating a
  1698.  * file, with what modes to create
  1699.  * it? */
  1700. {
  1701.     Tcl_Channel ret;
  1702.     int mode, seekFlag;
  1703.     Tcl_Obj *pathPtr;
  1704.     mode = TclGetOpenMode(interp, modeString, &seekFlag);
  1705.     if (mode == -1) {
  1706. return NULL;
  1707.     }
  1708.     pathPtr = Tcl_NewStringObj(fileName, -1);
  1709.     Tcl_IncrRefCount(pathPtr);
  1710.     ret = TclpOpenFileChannel(interp, pathPtr, mode, permissions);
  1711.     Tcl_DecrRefCount(pathPtr);
  1712.     if (ret != NULL) {
  1713. if (seekFlag) {
  1714.     if (Tcl_Seek(ret, (Tcl_WideInt)0, SEEK_END) < (Tcl_WideInt)0) {
  1715. if (interp != (Tcl_Interp *) NULL) {
  1716.     Tcl_AppendResult(interp,
  1717.       "could not seek to end of file while opening "",
  1718.       fileName, "": ", 
  1719.       Tcl_PosixError(interp), (char *) NULL);
  1720. }
  1721. Tcl_Close(NULL, ret);
  1722. return NULL;
  1723.     }
  1724. }
  1725.     }
  1726.     return ret;
  1727. }
  1728. static Tcl_Channel
  1729. TestOpenFileChannelProc1(interp, fileName, modeString, permissions)
  1730.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  1731.                                          * can be NULL. */
  1732.     CONST char *fileName;               /* Name of file to open. */
  1733.     CONST char *modeString;             /* A list of POSIX open modes or
  1734.                                          * a string such as "rw". */
  1735.     int permissions;                    /* If the open involves creating a
  1736.                                          * file, with what modes to create
  1737.                                          * it? */
  1738. {
  1739.     CONST char *expectname="testOpenFileChannel1%.fil";
  1740.     Tcl_DString ds;
  1741.     
  1742.     Tcl_DStringInit(&ds);
  1743.     Tcl_JoinPath(1, &expectname, &ds);
  1744.     if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
  1745. Tcl_DStringFree(&ds);
  1746. return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel1%__.fil",
  1747. modeString, permissions));
  1748.     } else {
  1749. Tcl_DStringFree(&ds);
  1750. return (NULL);
  1751.     }
  1752. }
  1753. static Tcl_Channel
  1754. TestOpenFileChannelProc2(interp, fileName, modeString, permissions)
  1755.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  1756.                                          * can be NULL. */
  1757.     CONST char *fileName;               /* Name of file to open. */
  1758.     CONST char *modeString;             /* A list of POSIX open modes or
  1759.                                          * a string such as "rw". */
  1760.     int permissions;                    /* If the open involves creating a
  1761.                                          * file, with what modes to create
  1762.                                          * it? */
  1763. {
  1764.     CONST char *expectname="testOpenFileChannel2%.fil";
  1765.     Tcl_DString ds;
  1766.     
  1767.     Tcl_DStringInit(&ds);
  1768.     Tcl_JoinPath(1, &expectname, &ds);
  1769.     if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
  1770. Tcl_DStringFree(&ds);
  1771. return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel2%__.fil",
  1772. modeString, permissions));
  1773.     } else {
  1774. Tcl_DStringFree(&ds);
  1775. return (NULL);
  1776.     }
  1777. }
  1778. static Tcl_Channel
  1779. TestOpenFileChannelProc3(interp, fileName, modeString, permissions)
  1780.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  1781.                                          * can be NULL. */
  1782.     CONST char *fileName;               /* Name of file to open. */
  1783.     CONST char *modeString;             /* A list of POSIX open modes or
  1784.                                          * a string such as "rw". */
  1785.     int permissions;                    /* If the open involves creating a
  1786.                                          * file, with what modes to create
  1787.                                          * it? */
  1788. {
  1789.     CONST char *expectname="testOpenFileChannel3%.fil";
  1790.     Tcl_DString ds;
  1791.     
  1792.     Tcl_DStringInit(&ds);
  1793.     Tcl_JoinPath(1, &expectname, &ds);
  1794.     if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
  1795. Tcl_DStringFree(&ds);
  1796. return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel3%__.fil",
  1797. modeString, permissions));
  1798.     } else {
  1799. Tcl_DStringFree(&ds);
  1800. return (NULL);
  1801.     }
  1802. }
  1803. /*
  1804.  *----------------------------------------------------------------------
  1805.  *
  1806.  * TestChannelCmd --
  1807.  *
  1808.  * Implements the Tcl "testchannel" debugging command and its
  1809.  * subcommands. This is part of the testing environment.
  1810.  *
  1811.  * Results:
  1812.  * A standard Tcl result.
  1813.  *
  1814.  * Side effects:
  1815.  * None.
  1816.  *
  1817.  *----------------------------------------------------------------------
  1818.  */
  1819. /* ARGSUSED */
  1820. static int
  1821. TestChannelCmd(clientData, interp, argc, argv)
  1822.     ClientData clientData; /* Not used. */
  1823.     Tcl_Interp *interp; /* Interpreter for result. */
  1824.     int argc; /* Count of additional args. */
  1825.     CONST char **argv; /* Additional arg strings. */
  1826. {
  1827.     CONST char *cmdName; /* Sub command. */
  1828.     Tcl_HashTable *hTblPtr; /* Hash table of channels. */
  1829.     Tcl_HashSearch hSearch; /* Search variable. */
  1830.     Tcl_HashEntry *hPtr; /* Search variable. */
  1831.     Channel *chanPtr; /* The actual channel. */
  1832.     ChannelState *statePtr; /* state info for channel */
  1833.     Tcl_Channel chan; /* The opaque type. */
  1834.     size_t len; /* Length of subcommand string. */
  1835.     int IOQueued; /* How much IO is queued inside channel? */
  1836.     ChannelBuffer *bufPtr; /* For iterating over queued IO. */
  1837.     char buf[TCL_INTEGER_SPACE];/* For sprintf. */
  1838.     int mode; /* rw mode of the channel */
  1839.     
  1840.     if (argc < 2) {
  1841.         Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  1842.                 " subcommand ?additional args..?"", (char *) NULL);
  1843.         return TCL_ERROR;
  1844.     }
  1845.     cmdName = argv[1];
  1846.     len = strlen(cmdName);
  1847.     chanPtr = (Channel *) NULL;
  1848.     if (argc > 2) {
  1849.         chan = Tcl_GetChannel(interp, argv[2], &mode);
  1850.         if (chan == (Tcl_Channel) NULL) {
  1851.             return TCL_ERROR;
  1852.         }
  1853.         chanPtr = (Channel *) chan;
  1854. statePtr = chanPtr->state;
  1855.         chanPtr = statePtr->topChanPtr;
  1856. chan = (Tcl_Channel) chanPtr;
  1857.     } else {
  1858. /* lint */
  1859. statePtr = NULL;
  1860. chan = NULL;
  1861.     }
  1862.     if ((cmdName[0] == 'c') && (strncmp(cmdName, "cut", len) == 0)) {
  1863.         if (argc != 3) {
  1864.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  1865.                     " cut channelName"", (char *) NULL);
  1866.             return TCL_ERROR;
  1867.         }
  1868.         Tcl_CutChannel(chan);
  1869.         return TCL_OK;
  1870.     }
  1871.     if ((cmdName[0] == 'c') &&
  1872.     (strncmp(cmdName, "clearchannelhandlers", len) == 0)) {
  1873.         if (argc != 3) {
  1874.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  1875.                     " clearchannelhandlers channelName"", (char *) NULL);
  1876.             return TCL_ERROR;
  1877.         }
  1878.         Tcl_ClearChannelHandlers(chan);
  1879.         return TCL_OK;
  1880.     }
  1881.     if ((cmdName[0] == 'i') && (strncmp(cmdName, "info", len) == 0)) {
  1882.         if (argc != 3) {
  1883.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  1884.                     " info channelName"", (char *) NULL);
  1885.             return TCL_ERROR;
  1886.         }
  1887.         Tcl_AppendElement(interp, argv[2]);
  1888.         Tcl_AppendElement(interp, Tcl_ChannelName(chanPtr->typePtr));
  1889.         if (statePtr->flags & TCL_READABLE) {
  1890.             Tcl_AppendElement(interp, "read");
  1891.         } else {
  1892.             Tcl_AppendElement(interp, "");
  1893.         }
  1894.         if (statePtr->flags & TCL_WRITABLE) {
  1895.             Tcl_AppendElement(interp, "write");
  1896.         } else {
  1897.             Tcl_AppendElement(interp, "");
  1898.         }
  1899.         if (statePtr->flags & CHANNEL_NONBLOCKING) {
  1900.             Tcl_AppendElement(interp, "nonblocking");
  1901.         } else {
  1902.             Tcl_AppendElement(interp, "blocking");
  1903.         }
  1904.         if (statePtr->flags & CHANNEL_LINEBUFFERED) {
  1905.             Tcl_AppendElement(interp, "line");
  1906.         } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
  1907.             Tcl_AppendElement(interp, "none");
  1908.         } else {
  1909.             Tcl_AppendElement(interp, "full");
  1910.         }
  1911.         if (statePtr->flags & BG_FLUSH_SCHEDULED) {
  1912.             Tcl_AppendElement(interp, "async_flush");
  1913.         } else {
  1914.             Tcl_AppendElement(interp, "");
  1915.         }
  1916.         if (statePtr->flags & CHANNEL_EOF) {
  1917.             Tcl_AppendElement(interp, "eof");
  1918.         } else {
  1919.             Tcl_AppendElement(interp, "");
  1920.         }
  1921.         if (statePtr->flags & CHANNEL_BLOCKED) {
  1922.             Tcl_AppendElement(interp, "blocked");
  1923.         } else {
  1924.             Tcl_AppendElement(interp, "unblocked");
  1925.         }
  1926.         if (statePtr->inputTranslation == TCL_TRANSLATE_AUTO) {
  1927.             Tcl_AppendElement(interp, "auto");
  1928.             if (statePtr->flags & INPUT_SAW_CR) {
  1929.                 Tcl_AppendElement(interp, "saw_cr");
  1930.             } else {
  1931.                 Tcl_AppendElement(interp, "");
  1932.             }
  1933.         } else if (statePtr->inputTranslation == TCL_TRANSLATE_LF) {
  1934.             Tcl_AppendElement(interp, "lf");
  1935.             Tcl_AppendElement(interp, "");
  1936.         } else if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
  1937.             Tcl_AppendElement(interp, "cr");
  1938.             Tcl_AppendElement(interp, "");
  1939.         } else if (statePtr->inputTranslation == TCL_TRANSLATE_CRLF) {
  1940.             Tcl_AppendElement(interp, "crlf");
  1941.             if (statePtr->flags & INPUT_SAW_CR) {
  1942.                 Tcl_AppendElement(interp, "queued_cr");
  1943.             } else {
  1944.                 Tcl_AppendElement(interp, "");
  1945.             }
  1946.         }
  1947.         if (statePtr->outputTranslation == TCL_TRANSLATE_AUTO) {
  1948.             Tcl_AppendElement(interp, "auto");
  1949.         } else if (statePtr->outputTranslation == TCL_TRANSLATE_LF) {
  1950.             Tcl_AppendElement(interp, "lf");
  1951.         } else if (statePtr->outputTranslation == TCL_TRANSLATE_CR) {
  1952.             Tcl_AppendElement(interp, "cr");
  1953.         } else if (statePtr->outputTranslation == TCL_TRANSLATE_CRLF) {
  1954.             Tcl_AppendElement(interp, "crlf");
  1955.         }
  1956.         for (IOQueued = 0, bufPtr = statePtr->inQueueHead;
  1957.      bufPtr != (ChannelBuffer *) NULL;
  1958.      bufPtr = bufPtr->nextPtr) {
  1959.             IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
  1960.         }
  1961.         TclFormatInt(buf, IOQueued);
  1962.         Tcl_AppendElement(interp, buf);
  1963.         
  1964.         IOQueued = 0;
  1965.         if (statePtr->curOutPtr != (ChannelBuffer *) NULL) {
  1966.             IOQueued = statePtr->curOutPtr->nextAdded -
  1967.                 statePtr->curOutPtr->nextRemoved;
  1968.         }
  1969.         for (bufPtr = statePtr->outQueueHead;
  1970.      bufPtr != (ChannelBuffer *) NULL;
  1971.      bufPtr = bufPtr->nextPtr) {
  1972.             IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
  1973.         }
  1974.         TclFormatInt(buf, IOQueued);
  1975.         Tcl_AppendElement(interp, buf);
  1976.         
  1977.         TclFormatInt(buf, (int)Tcl_Tell((Tcl_Channel) chanPtr));
  1978.         Tcl_AppendElement(interp, buf);
  1979.         TclFormatInt(buf, statePtr->refCount);
  1980.         Tcl_AppendElement(interp, buf);
  1981.         return TCL_OK;
  1982.     }
  1983.     if ((cmdName[0] == 'i') &&
  1984.             (strncmp(cmdName, "inputbuffered", len) == 0)) {
  1985.         if (argc != 3) {
  1986.             Tcl_AppendResult(interp, "channel name required",
  1987.                     (char *) NULL);
  1988.             return TCL_ERROR;
  1989.         }
  1990.         
  1991.         for (IOQueued = 0, bufPtr = statePtr->inQueueHead;
  1992.      bufPtr != (ChannelBuffer *) NULL;
  1993.      bufPtr = bufPtr->nextPtr) {
  1994.             IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
  1995.         }
  1996.         TclFormatInt(buf, IOQueued);
  1997.         Tcl_AppendResult(interp, buf, (char *) NULL);
  1998.         return TCL_OK;
  1999.     }
  2000.     if ((cmdName[0] == 'i') && (strncmp(cmdName, "isshared", len) == 0)) {
  2001.         if (argc != 3) {
  2002.             Tcl_AppendResult(interp, "channel name required", (char *) NULL);
  2003.             return TCL_ERROR;
  2004.         }
  2005.         
  2006.         TclFormatInt(buf, Tcl_IsChannelShared(chan));
  2007.         Tcl_AppendResult(interp, buf, (char *) NULL);
  2008.         return TCL_OK;
  2009.     }
  2010.     if ((cmdName[0] == 'i') && (strncmp(cmdName, "isstandard", len) == 0)) {
  2011. if (argc != 3) {
  2012.     Tcl_AppendResult(interp, "channel name required", (char *) NULL);
  2013.     return TCL_ERROR;
  2014. }
  2015. TclFormatInt(buf, Tcl_IsStandardChannel(chan));
  2016. Tcl_AppendResult(interp, buf, (char *) NULL);
  2017. return TCL_OK;
  2018.     }
  2019.     if ((cmdName[0] == 'm') && (strncmp(cmdName, "mode", len) == 0)) {
  2020.         if (argc != 3) {
  2021.             Tcl_AppendResult(interp, "channel name required",
  2022.                     (char *) NULL);
  2023.             return TCL_ERROR;
  2024.         }
  2025.         
  2026.         if (statePtr->flags & TCL_READABLE) {
  2027.             Tcl_AppendElement(interp, "read");
  2028.         } else {
  2029.             Tcl_AppendElement(interp, "");
  2030.         }
  2031.         if (statePtr->flags & TCL_WRITABLE) {
  2032.             Tcl_AppendElement(interp, "write");
  2033.         } else {
  2034.             Tcl_AppendElement(interp, "");
  2035.         }
  2036.         return TCL_OK;
  2037.     }
  2038.     
  2039.     if ((cmdName[0] == 'm') && (strncmp(cmdName, "mthread", len) == 0)) {
  2040.         if (argc != 3) {
  2041.             Tcl_AppendResult(interp, "channel name required",
  2042.                     (char *) NULL);
  2043.             return TCL_ERROR;
  2044.         }
  2045.         TclFormatInt(buf, (long) Tcl_GetChannelThread(chan));
  2046.         Tcl_AppendResult(interp, buf, (char *) NULL);
  2047.         return TCL_OK;
  2048.     }
  2049.     if ((cmdName[0] == 'n') && (strncmp(cmdName, "name", len) == 0)) {
  2050.         if (argc != 3) {
  2051.             Tcl_AppendResult(interp, "channel name required",
  2052.                     (char *) NULL);
  2053.             return TCL_ERROR;
  2054.         }
  2055.         Tcl_AppendResult(interp, statePtr->channelName, (char *) NULL);
  2056.         return TCL_OK;
  2057.     }
  2058.     if ((cmdName[0] == 'o') && (strncmp(cmdName, "open", len) == 0)) {
  2059.         hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
  2060.         if (hTblPtr == (Tcl_HashTable *) NULL) {
  2061.             return TCL_OK;
  2062.         }
  2063.         for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
  2064.      hPtr != (Tcl_HashEntry *) NULL;
  2065.      hPtr = Tcl_NextHashEntry(&hSearch)) {
  2066.             Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
  2067.         }
  2068.         return TCL_OK;
  2069.     }
  2070.     if ((cmdName[0] == 'o') &&
  2071.             (strncmp(cmdName, "outputbuffered", len) == 0)) {
  2072.         if (argc != 3) {
  2073.             Tcl_AppendResult(interp, "channel name required",
  2074.                     (char *) NULL);
  2075.             return TCL_ERROR;
  2076.         }
  2077.         IOQueued = 0;
  2078.         if (statePtr->curOutPtr != (ChannelBuffer *) NULL) {
  2079.             IOQueued = statePtr->curOutPtr->nextAdded -
  2080.                 statePtr->curOutPtr->nextRemoved;
  2081.         }
  2082.         for (bufPtr = statePtr->outQueueHead;
  2083.      bufPtr != (ChannelBuffer *) NULL;
  2084.      bufPtr = bufPtr->nextPtr) {
  2085.             IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
  2086.         }
  2087.         TclFormatInt(buf, IOQueued);
  2088.         Tcl_AppendResult(interp, buf, (char *) NULL);
  2089.         return TCL_OK;
  2090.     }
  2091.     if ((cmdName[0] == 'q') &&
  2092.             (strncmp(cmdName, "queuedcr", len) == 0)) {
  2093.         if (argc != 3) {
  2094.             Tcl_AppendResult(interp, "channel name required",
  2095.                     (char *) NULL);
  2096.             return TCL_ERROR;
  2097.         }
  2098.         Tcl_AppendResult(interp,
  2099.                 (statePtr->flags & INPUT_SAW_CR) ? "1" : "0",
  2100.                 (char *) NULL);
  2101.         return TCL_OK;
  2102.     }
  2103.     if ((cmdName[0] == 'r') && (strncmp(cmdName, "readable", len) == 0)) {
  2104.         hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
  2105.         if (hTblPtr == (Tcl_HashTable *) NULL) {
  2106.             return TCL_OK;
  2107.         }
  2108.         for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
  2109.      hPtr != (Tcl_HashEntry *) NULL;
  2110.      hPtr = Tcl_NextHashEntry(&hSearch)) {
  2111.             chanPtr  = (Channel *) Tcl_GetHashValue(hPtr);
  2112.     statePtr = chanPtr->state;
  2113.             if (statePtr->flags & TCL_READABLE) {
  2114.                 Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
  2115.             }
  2116.         }
  2117.         return TCL_OK;
  2118.     }
  2119.     if ((cmdName[0] == 'r') && (strncmp(cmdName, "refcount", len) == 0)) {
  2120.         if (argc != 3) {
  2121.             Tcl_AppendResult(interp, "channel name required",
  2122.                     (char *) NULL);
  2123.             return TCL_ERROR;
  2124.         }
  2125.         
  2126.         TclFormatInt(buf, statePtr->refCount);
  2127.         Tcl_AppendResult(interp, buf, (char *) NULL);
  2128.         return TCL_OK;
  2129.     }
  2130.     if ((cmdName[0] == 's') && (strncmp(cmdName, "splice", len) == 0)) {
  2131.         if (argc != 3) {
  2132.             Tcl_AppendResult(interp, "channel name required", (char *) NULL);
  2133.             return TCL_ERROR;
  2134.         }
  2135.         Tcl_SpliceChannel(chan);
  2136.         return TCL_OK;
  2137.     }
  2138.     if ((cmdName[0] == 't') && (strncmp(cmdName, "type", len) == 0)) {
  2139.         if (argc != 3) {
  2140.             Tcl_AppendResult(interp, "channel name required",
  2141.                     (char *) NULL);
  2142.             return TCL_ERROR;
  2143.         }
  2144.         Tcl_AppendResult(interp, Tcl_ChannelName(chanPtr->typePtr),
  2145. (char *) NULL);
  2146.         return TCL_OK;
  2147.     }
  2148.     if ((cmdName[0] == 'w') && (strncmp(cmdName, "writable", len) == 0)) {
  2149.         hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
  2150.         if (hTblPtr == (Tcl_HashTable *) NULL) {
  2151.             return TCL_OK;
  2152.         }
  2153.         for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
  2154.      hPtr != (Tcl_HashEntry *) NULL;
  2155.      hPtr = Tcl_NextHashEntry(&hSearch)) {
  2156.             chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
  2157.     statePtr = chanPtr->state;
  2158.             if (statePtr->flags & TCL_WRITABLE) {
  2159.                 Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
  2160.             }
  2161.         }
  2162.         return TCL_OK;
  2163.     }
  2164.     if ((cmdName[0] == 't') && (strncmp(cmdName, "transform", len) == 0)) {
  2165. /*
  2166.  * Syntax: transform channel -command command
  2167.  */
  2168.         if (argc != 5) {
  2169.     Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2170.     " transform channelId -command cmd"", (char *) NULL);
  2171.             return TCL_ERROR;
  2172.         }
  2173. if (strcmp(argv[3], "-command") != 0) {
  2174.     Tcl_AppendResult(interp, "bad argument "", argv[3],
  2175.     "": should be "-command"", (char *) NULL);
  2176.     return TCL_ERROR;
  2177. }
  2178. return TclChannelTransform(interp, chan,
  2179. Tcl_NewStringObj(argv[4], -1));
  2180.     }
  2181.     if ((cmdName[0] == 'u') && (strncmp(cmdName, "unstack", len) == 0)) {
  2182. /*
  2183.  * Syntax: unstack channel
  2184.  */
  2185.         if (argc != 3) {
  2186.     Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2187.     " unstack channel"", (char *) NULL);
  2188.             return TCL_ERROR;
  2189.         }
  2190. return Tcl_UnstackChannel(interp, chan);
  2191.     }
  2192.     Tcl_AppendResult(interp, "bad option "", cmdName, "": should be ",
  2193.             "cut, clearchannelhandlers, info, isshared, mode, open, "
  2194.     "readable, splice, writable, transform, unstack",
  2195.             (char *) NULL);
  2196.     return TCL_ERROR;
  2197. }
  2198. /*
  2199.  *----------------------------------------------------------------------
  2200.  *
  2201.  * TestChannelEventCmd --
  2202.  *
  2203.  * This procedure implements the "testchannelevent" command. It is
  2204.  * used to test the Tcl channel event mechanism.
  2205.  *
  2206.  * Results:
  2207.  * A standard Tcl result.
  2208.  *
  2209.  * Side effects:
  2210.  * Creates, deletes and returns channel event handlers.
  2211.  *
  2212.  *----------------------------------------------------------------------
  2213.  */
  2214. /* ARGSUSED */
  2215. static int
  2216. TestChannelEventCmd(dummy, interp, argc, argv)
  2217.     ClientData dummy; /* Not used. */
  2218.     Tcl_Interp *interp; /* Current interpreter. */
  2219.     int argc; /* Number of arguments. */
  2220.     CONST char **argv; /* Argument strings. */
  2221. {
  2222.     Tcl_Obj *resultListPtr;
  2223.     Channel *chanPtr;
  2224.     ChannelState *statePtr; /* state info for channel */
  2225.     EventScriptRecord *esPtr, *prevEsPtr, *nextEsPtr;
  2226.     CONST char *cmd;
  2227.     int index, i, mask, len;
  2228.     if ((argc < 3) || (argc > 5)) {
  2229.         Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2230.                 " channelName cmd ?arg1? ?arg2?"", (char *) NULL);
  2231.         return TCL_ERROR;
  2232.     }
  2233.     chanPtr = (Channel *) Tcl_GetChannel(interp, argv[1], NULL);
  2234.     if (chanPtr == (Channel *) NULL) {
  2235.         return TCL_ERROR;
  2236.     }
  2237.     statePtr = chanPtr->state;
  2238.     cmd = argv[2];
  2239.     len = strlen(cmd);
  2240.     if ((cmd[0] == 'a') && (strncmp(cmd, "add", (unsigned) len) == 0)) {
  2241.         if (argc != 5) {
  2242.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2243.                     " channelName add eventSpec script"", (char *) NULL);
  2244.             return TCL_ERROR;
  2245.         }
  2246.         if (strcmp(argv[3], "readable") == 0) {
  2247.             mask = TCL_READABLE;
  2248.         } else if (strcmp(argv[3], "writable") == 0) {
  2249.             mask = TCL_WRITABLE;
  2250.         } else if (strcmp(argv[3], "none") == 0) {
  2251.             mask = 0;
  2252. } else {
  2253.             Tcl_AppendResult(interp, "bad event name "", argv[3],
  2254.                     "": must be readable, writable, or none", (char *) NULL);
  2255.             return TCL_ERROR;
  2256.         }
  2257.         esPtr = (EventScriptRecord *) ckalloc((unsigned)
  2258.                 sizeof(EventScriptRecord));
  2259.         esPtr->nextPtr = statePtr->scriptRecordPtr;
  2260.         statePtr->scriptRecordPtr = esPtr;
  2261.         
  2262.         esPtr->chanPtr = chanPtr;
  2263.         esPtr->interp = interp;
  2264.         esPtr->mask = mask;
  2265. esPtr->scriptPtr = Tcl_NewStringObj(argv[4], -1);
  2266. Tcl_IncrRefCount(esPtr->scriptPtr);
  2267.         Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
  2268.                 TclChannelEventScriptInvoker, (ClientData) esPtr);
  2269.         
  2270.         return TCL_OK;
  2271.     }
  2272.     if ((cmd[0] == 'd') && (strncmp(cmd, "delete", (unsigned) len) == 0)) {
  2273.         if (argc != 4) {
  2274.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2275.                     " channelName delete index"", (char *) NULL);
  2276.             return TCL_ERROR;
  2277.         }
  2278.         if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
  2279.             return TCL_ERROR;
  2280.         }
  2281.         if (index < 0) {
  2282.             Tcl_AppendResult(interp, "bad event index: ", argv[3],
  2283.                     ": must be nonnegative", (char *) NULL);
  2284.             return TCL_ERROR;
  2285.         }
  2286.         for (i = 0, esPtr = statePtr->scriptRecordPtr;
  2287.      (i < index) && (esPtr != (EventScriptRecord *) NULL);
  2288.      i++, esPtr = esPtr->nextPtr) {
  2289.     /* Empty loop body. */
  2290.         }
  2291.         if (esPtr == (EventScriptRecord *) NULL) {
  2292.             Tcl_AppendResult(interp, "bad event index ", argv[3],
  2293.                     ": out of range", (char *) NULL);
  2294.             return TCL_ERROR;
  2295.         }
  2296.         if (esPtr == statePtr->scriptRecordPtr) {
  2297.             statePtr->scriptRecordPtr = esPtr->nextPtr;
  2298.         } else {
  2299.             for (prevEsPtr = statePtr->scriptRecordPtr;
  2300.  (prevEsPtr != (EventScriptRecord *) NULL) &&
  2301.      (prevEsPtr->nextPtr != esPtr);
  2302.  prevEsPtr = prevEsPtr->nextPtr) {
  2303.                 /* Empty loop body. */
  2304.             }
  2305.             if (prevEsPtr == (EventScriptRecord *) NULL) {
  2306.                 panic("TestChannelEventCmd: damaged event script list");
  2307.             }
  2308.             prevEsPtr->nextPtr = esPtr->nextPtr;
  2309.         }
  2310.         Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
  2311.                 TclChannelEventScriptInvoker, (ClientData) esPtr);
  2312. Tcl_DecrRefCount(esPtr->scriptPtr);
  2313.         ckfree((char *) esPtr);
  2314.         return TCL_OK;
  2315.     }
  2316.     if ((cmd[0] == 'l') && (strncmp(cmd, "list", (unsigned) len) == 0)) {
  2317.         if (argc != 3) {
  2318.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2319.                     " channelName list"", (char *) NULL);
  2320.             return TCL_ERROR;
  2321.         }
  2322. resultListPtr = Tcl_GetObjResult(interp);
  2323.         for (esPtr = statePtr->scriptRecordPtr;
  2324.      esPtr != (EventScriptRecord *) NULL;
  2325.      esPtr = esPtr->nextPtr) {
  2326.     if (esPtr->mask) {
  2327.           Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
  2328.     (esPtr->mask == TCL_READABLE) ? "readable" : "writable", -1));
  2329.       } else {
  2330.           Tcl_ListObjAppendElement(interp, resultListPtr, 
  2331. Tcl_NewStringObj("none", -1));
  2332.     }
  2333.        Tcl_ListObjAppendElement(interp, resultListPtr, esPtr->scriptPtr);
  2334.         }
  2335. Tcl_SetObjResult(interp, resultListPtr);
  2336.         return TCL_OK;
  2337.     }
  2338.     if ((cmd[0] == 'r') && (strncmp(cmd, "removeall", (unsigned) len) == 0)) {
  2339.         if (argc != 3) {
  2340.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2341.                     " channelName removeall"", (char *) NULL);
  2342.             return TCL_ERROR;
  2343.         }
  2344.         for (esPtr = statePtr->scriptRecordPtr;
  2345.      esPtr != (EventScriptRecord *) NULL;
  2346.      esPtr = nextEsPtr) {
  2347.             nextEsPtr = esPtr->nextPtr;
  2348.             Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
  2349.                     TclChannelEventScriptInvoker, (ClientData) esPtr);
  2350.     Tcl_DecrRefCount(esPtr->scriptPtr);
  2351.             ckfree((char *) esPtr);
  2352.         }
  2353.         statePtr->scriptRecordPtr = (EventScriptRecord *) NULL;
  2354.         return TCL_OK;
  2355.     }
  2356.     if  ((cmd[0] == 's') && (strncmp(cmd, "set", (unsigned) len) == 0)) {
  2357.         if (argc != 5) {
  2358.             Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  2359.                     " channelName delete index event"", (char *) NULL);
  2360.             return TCL_ERROR;
  2361.         }
  2362.         if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
  2363.             return TCL_ERROR;
  2364.         }
  2365.         if (index < 0) {
  2366.             Tcl_AppendResult(interp, "bad event index: ", argv[3],
  2367.                     ": must be nonnegative", (char *) NULL);
  2368.             return TCL_ERROR;
  2369.         }
  2370.         for (i = 0, esPtr = statePtr->scriptRecordPtr;
  2371.      (i < index) && (esPtr != (EventScriptRecord *) NULL);
  2372.      i++, esPtr = esPtr->nextPtr) {
  2373.     /* Empty loop body. */
  2374.         }
  2375.         if (esPtr == (EventScriptRecord *) NULL) {
  2376.             Tcl_AppendResult(interp, "bad event index ", argv[3],
  2377.                     ": out of range", (char *) NULL);
  2378.             return TCL_ERROR;
  2379.         }
  2380.         if (strcmp(argv[4], "readable") == 0) {
  2381.             mask = TCL_READABLE;
  2382.         } else if (strcmp(argv[4], "writable") == 0) {
  2383.             mask = TCL_WRITABLE;
  2384.         } else if (strcmp(argv[4], "none") == 0) {
  2385.             mask = 0;
  2386. } else {
  2387.             Tcl_AppendResult(interp, "bad event name "", argv[4],
  2388.                     "": must be readable, writable, or none", (char *) NULL);
  2389.             return TCL_ERROR;
  2390.         }
  2391. esPtr->mask = mask;
  2392.         Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
  2393.                 TclChannelEventScriptInvoker, (ClientData) esPtr);
  2394. return TCL_OK;
  2395.     }    
  2396.     Tcl_AppendResult(interp, "bad command ", cmd, ", must be one of ",
  2397.             "add, delete, list, set, or removeall", (char *) NULL);
  2398.     return TCL_ERROR;
  2399. }
  2400. /*
  2401.  *----------------------------------------------------------------------
  2402.  *
  2403.  * TestWrongNumArgsObjCmd --
  2404.  *
  2405.  * Test the Tcl_WrongNumArgs function.
  2406.  *
  2407.  * Results:
  2408.  * Standard Tcl result.
  2409.  *
  2410.  * Side effects:
  2411.  * Sets interpreter result.
  2412.  *
  2413.  *----------------------------------------------------------------------
  2414.  */
  2415. static int
  2416. TestWrongNumArgsObjCmd(dummy, interp, objc, objv)
  2417.     ClientData dummy; /* Not used. */
  2418.     Tcl_Interp *interp; /* Current interpreter. */
  2419.     int objc; /* Number of arguments. */
  2420.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  2421. {
  2422.     int i, length;
  2423.     char *msg;
  2424.     if (objc < 3) {
  2425. /*
  2426.  * Don't use Tcl_WrongNumArgs here, as that is the function
  2427.  * we want to test!
  2428.  */
  2429. Tcl_SetResult(interp, "insufficient arguments", TCL_STATIC);
  2430. return TCL_ERROR;
  2431.     }
  2432.     
  2433.     if (Tcl_GetIntFromObj(interp, objv[1], &i) != TCL_OK) {
  2434. return TCL_ERROR;
  2435.     }
  2436.     msg = Tcl_GetStringFromObj(objv[2], &length);
  2437.     if (length == 0) {
  2438. msg = NULL;
  2439.     }
  2440.     
  2441.     if (i > objc - 3) {
  2442. /*
  2443.  * Asked for more arguments than were given.
  2444.  */
  2445. Tcl_SetResult(interp, "insufficient arguments", TCL_STATIC);
  2446. return TCL_ERROR;
  2447.     }
  2448.     Tcl_WrongNumArgs(interp, i, &(objv[3]), msg);
  2449.     return TCL_OK;
  2450. }
  2451. /*
  2452.  *----------------------------------------------------------------------
  2453.  *
  2454.  * TestGetIndexFromObjStructObjCmd --
  2455.  *
  2456.  * Test the Tcl_GetIndexFromObjStruct function.
  2457.  *
  2458.  * Results:
  2459.  * Standard Tcl result.
  2460.  *
  2461.  * Side effects:
  2462.  * Sets interpreter result.
  2463.  *
  2464.  *----------------------------------------------------------------------
  2465.  */
  2466. static int
  2467. TestGetIndexFromObjStructObjCmd(dummy, interp, objc, objv)
  2468.     ClientData dummy; /* Not used. */
  2469.     Tcl_Interp *interp; /* Current interpreter. */
  2470.     int objc; /* Number of arguments. */
  2471.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  2472. {
  2473.     char *ary[] = {
  2474. "a", "b", "c", "d", "e", "f", (char *)NULL,(char *)NULL
  2475.     };
  2476.     int idx,target;
  2477.     if (objc != 3) {
  2478. Tcl_WrongNumArgs(interp, 1, objv, "argument targetvalue");
  2479. return TCL_ERROR;
  2480.     }
  2481.     if (Tcl_GetIndexFromObjStruct(interp, objv[1], ary, 2*sizeof(char *),
  2482.   "dummy", 0, &idx) != TCL_OK) {
  2483. return TCL_ERROR;
  2484.     }
  2485.     if (Tcl_GetIntFromObj(interp, objv[2], &target) != TCL_OK) {
  2486. return TCL_ERROR;
  2487.     }
  2488.     if (idx != target) {
  2489. char buffer[64];
  2490. sprintf(buffer, "%d", idx);
  2491. Tcl_AppendResult(interp, "index value comparison failed: got ",
  2492.  buffer, NULL);
  2493. sprintf(buffer, "%d", target);
  2494. Tcl_AppendResult(interp, " when ", buffer, " expected", NULL);
  2495. return TCL_ERROR;
  2496.     }
  2497.     Tcl_WrongNumArgs(interp, 3, objv, NULL);
  2498.     return TCL_OK;
  2499. }
  2500. /*
  2501.  *----------------------------------------------------------------------
  2502.  *
  2503.  * TestFilesystemObjCmd --
  2504.  *
  2505.  * This procedure implements the "testfilesystem" command.  It is
  2506.  * used to test Tcl_FSRegister, Tcl_FSUnregister, and can be used
  2507.  * to test that the pluggable filesystem works.
  2508.  *
  2509.  * Results:
  2510.  * A standard Tcl result.
  2511.  *
  2512.  * Side effects:
  2513.  * Inserts or removes a filesystem from Tcl's stack.
  2514.  *
  2515.  *----------------------------------------------------------------------
  2516.  */
  2517. static int
  2518. TestFilesystemObjCmd(dummy, interp, objc, objv)
  2519.     ClientData dummy;
  2520.     Tcl_Interp *interp;
  2521.     int objc;
  2522.     Tcl_Obj *CONST objv[];
  2523. {
  2524.     int res, boolVal;
  2525.     char *msg;
  2526.     
  2527.     if (objc != 2) {
  2528. Tcl_WrongNumArgs(interp, 1, objv, "boolean");
  2529. return TCL_ERROR;
  2530.     }
  2531.     if (Tcl_GetBooleanFromObj(interp, objv[1], &boolVal) != TCL_OK) {
  2532. return TCL_ERROR;
  2533.     }
  2534.     if (boolVal) {
  2535. res = Tcl_FSRegister((ClientData)interp, &testReportingFilesystem);
  2536. msg = (res == TCL_OK) ? "registered" : "failed";
  2537.     } else {
  2538. res = Tcl_FSUnregister(&testReportingFilesystem);
  2539. msg = (res == TCL_OK) ? "unregistered" : "failed";
  2540.     }
  2541.     Tcl_SetResult(interp, msg, TCL_VOLATILE);
  2542.     return res;
  2543. }
  2544. static int 
  2545. TestReportInFilesystem(Tcl_Obj *pathPtr, ClientData *clientDataPtr)
  2546. {
  2547.     static Tcl_Obj* lastPathPtr = NULL;
  2548.     
  2549.     if (pathPtr == lastPathPtr) {
  2550. /* Reject all files second time around */
  2551.         return -1;
  2552.     } else {
  2553. Tcl_Obj * newPathPtr;
  2554. /* Try to claim all files first time around */
  2555. newPathPtr = Tcl_DuplicateObj(pathPtr);
  2556. lastPathPtr = newPathPtr;
  2557. Tcl_IncrRefCount(newPathPtr);
  2558. if (Tcl_FSGetFileSystemForPath(newPathPtr) == NULL) {
  2559.     /* Nothing claimed it.  Therefore we don't either */
  2560.     Tcl_DecrRefCount(newPathPtr);
  2561.     lastPathPtr = NULL;
  2562.     return -1;
  2563. } else {
  2564.     lastPathPtr = NULL;
  2565.     *clientDataPtr = (ClientData) newPathPtr;
  2566.     return TCL_OK;
  2567. }
  2568.     }
  2569. }
  2570. /* 
  2571.  * Simple helper function to extract the native vfs representation of a
  2572.  * path object, or NULL if no such representation exists.
  2573.  */
  2574. static Tcl_Obj* 
  2575. TestReportGetNativePath(Tcl_Obj* pathObjPtr) {
  2576.     return (Tcl_Obj*) Tcl_FSGetInternalRep(pathObjPtr, &testReportingFilesystem);
  2577. }
  2578. static void 
  2579. TestReportFreeInternalRep(ClientData clientData) {
  2580.     Tcl_Obj *nativeRep = (Tcl_Obj*)clientData;
  2581.     if (nativeRep != NULL) {
  2582. /* Free the path */
  2583. Tcl_DecrRefCount(nativeRep);
  2584.     }
  2585. }
  2586. static ClientData 
  2587. TestReportDupInternalRep(ClientData clientData) {
  2588.     Tcl_Obj *original = (Tcl_Obj*)clientData;
  2589.     Tcl_IncrRefCount(original);
  2590.     return clientData;
  2591. }
  2592. static void
  2593. TestReport(cmd, path, arg2)
  2594.     CONST char* cmd;
  2595.     Tcl_Obj* path;
  2596.     Tcl_Obj* arg2;
  2597. {
  2598.     Tcl_Interp* interp = (Tcl_Interp*) Tcl_FSData(&testReportingFilesystem);
  2599.     if (interp == NULL) {
  2600. /* This is bad, but not much we can do about it */
  2601.     } else {
  2602. /* 
  2603.  * No idea why I decided to program this up using the
  2604.  * old string-based API, but there you go.  We should
  2605.  * convert it to objects.
  2606.  */
  2607. Tcl_SavedResult savedResult;
  2608. Tcl_DString ds;
  2609. Tcl_DStringInit(&ds);
  2610. Tcl_DStringAppend(&ds, "lappend filesystemReport ",-1);
  2611. Tcl_DStringStartSublist(&ds);
  2612. Tcl_DStringAppendElement(&ds, cmd);
  2613. if (path != NULL) {
  2614.     Tcl_DStringAppendElement(&ds, Tcl_GetString(path));
  2615. }
  2616. if (arg2 != NULL) {
  2617.     Tcl_DStringAppendElement(&ds, Tcl_GetString(arg2));
  2618. }
  2619. Tcl_DStringEndSublist(&ds);
  2620. Tcl_SaveResult(interp, &savedResult);
  2621. Tcl_Eval(interp, Tcl_DStringValue(&ds));
  2622. Tcl_DStringFree(&ds);
  2623. Tcl_RestoreResult(interp, &savedResult);
  2624.    }
  2625. }
  2626. static int
  2627. TestReportStat(path, buf)
  2628.     Tcl_Obj *path; /* Path of file to stat (in current CP). */
  2629.     Tcl_StatBuf *buf; /* Filled with results of stat call. */
  2630. {
  2631.     TestReport("stat",path, NULL);
  2632.     return Tcl_FSStat(TestReportGetNativePath(path),buf);
  2633. }
  2634. static int
  2635. TestReportLstat(path, buf)
  2636.     Tcl_Obj *path; /* Path of file to stat (in current CP). */
  2637.     Tcl_StatBuf *buf; /* Filled with results of stat call. */
  2638. {
  2639.     TestReport("lstat",path, NULL);
  2640.     return Tcl_FSLstat(TestReportGetNativePath(path),buf);
  2641. }
  2642. static int
  2643. TestReportAccess(path, mode)
  2644.     Tcl_Obj *path; /* Path of file to access (in current CP). */
  2645.     int mode;                   /* Permission setting. */
  2646. {
  2647.     TestReport("access",path,NULL);
  2648.     return Tcl_FSAccess(TestReportGetNativePath(path),mode);
  2649. }
  2650. static Tcl_Channel
  2651. TestReportOpenFileChannel(interp, fileName, mode, permissions)
  2652.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  2653.  * can be NULL. */
  2654.     Tcl_Obj *fileName;                  /* Name of file to open. */
  2655.     int mode;                           /* POSIX open mode. */
  2656.     int permissions;                    /* If the open involves creating a
  2657.  * file, with what modes to create
  2658.  * it? */
  2659. {
  2660.     TestReport("open",fileName, NULL);
  2661.     return TclpOpenFileChannel(interp, TestReportGetNativePath(fileName),
  2662.  mode, permissions);
  2663. }
  2664. static int
  2665. TestReportMatchInDirectory(interp, resultPtr, dirPtr, pattern, types)
  2666.     Tcl_Interp *interp; /* Interpreter to receive results. */
  2667.     Tcl_Obj *resultPtr; /* Object to lappend results. */
  2668.     Tcl_Obj *dirPtr;         /* Contains path to directory to search. */
  2669.     CONST char *pattern; /* Pattern to match against. */
  2670.     Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
  2671.  * May be NULL. */
  2672. {
  2673.     if (types != NULL && types->type & TCL_GLOB_TYPE_MOUNT) {
  2674. TestReport("matchmounts",dirPtr, NULL);
  2675. return TCL_OK;
  2676.     } else {
  2677. TestReport("matchindirectory",dirPtr, NULL);
  2678. return Tcl_FSMatchInDirectory(interp, resultPtr, 
  2679.       TestReportGetNativePath(dirPtr), pattern, 
  2680.       types);
  2681.     }
  2682. }
  2683. static int
  2684. TestReportChdir(dirName)
  2685.     Tcl_Obj *dirName;
  2686. {
  2687.     TestReport("chdir",dirName,NULL);
  2688.     return Tcl_FSChdir(TestReportGetNativePath(dirName));
  2689. }
  2690. static int
  2691. TestReportLoadFile(interp, fileName,  
  2692.    handlePtr, unloadProcPtr)
  2693.     Tcl_Interp *interp; /* Used for error reporting. */
  2694.     Tcl_Obj *fileName; /* Name of the file containing the desired
  2695.  * code. */
  2696.     Tcl_LoadHandle *handlePtr; /* Filled with token for dynamically loaded
  2697.  * file which will be passed back to 
  2698.  * (*unloadProcPtr)() to unload the file. */
  2699.     Tcl_FSUnloadFileProc **unloadProcPtr;
  2700. /* Filled with address of Tcl_FSUnloadFileProc
  2701.  * function which should be used for
  2702.  * this file. */
  2703. {
  2704.     TestReport("loadfile",fileName,NULL);
  2705.     return Tcl_FSLoadFile(interp, TestReportGetNativePath(fileName), NULL, NULL,
  2706.   NULL, NULL, handlePtr, unloadProcPtr);
  2707. }
  2708. static Tcl_Obj *
  2709. TestReportLink(path, to, linkType)
  2710.     Tcl_Obj *path; /* Path of file to readlink or link */
  2711.     Tcl_Obj *to; /* Path of file to link to, or NULL */
  2712.     int linkType;
  2713. {
  2714.     TestReport("link",path,to);
  2715.     return Tcl_FSLink(TestReportGetNativePath(path), to, linkType);
  2716. }
  2717. static int
  2718. TestReportRenameFile(src, dst)
  2719.     Tcl_Obj *src; /* Pathname of file or dir to be renamed
  2720.  * (UTF-8). */
  2721.     Tcl_Obj *dst; /* New pathname of file or directory
  2722.  * (UTF-8). */
  2723. {
  2724.     TestReport("renamefile",src,dst);
  2725.     return Tcl_FSRenameFile(TestReportGetNativePath(src), 
  2726.     TestReportGetNativePath(dst));
  2727. }
  2728. static int 
  2729. TestReportCopyFile(src, dst)
  2730.     Tcl_Obj *src; /* Pathname of file to be copied (UTF-8). */
  2731.     Tcl_Obj *dst; /* Pathname of file to copy to (UTF-8). */
  2732. {
  2733.     TestReport("copyfile",src,dst);
  2734.     return Tcl_FSCopyFile(TestReportGetNativePath(src), 
  2735.     TestReportGetNativePath(dst));
  2736. }
  2737. static int
  2738. TestReportDeleteFile(path)
  2739.     Tcl_Obj *path; /* Pathname of file to be removed (UTF-8). */
  2740. {
  2741.     TestReport("deletefile",path,NULL);
  2742.     return Tcl_FSDeleteFile(TestReportGetNativePath(path));
  2743. }
  2744. static int
  2745. TestReportCreateDirectory(path)
  2746.     Tcl_Obj *path; /* Pathname of directory to create (UTF-8). */
  2747. {
  2748.     TestReport("createdirectory",path,NULL);
  2749.     return Tcl_FSCreateDirectory(TestReportGetNativePath(path));
  2750. }
  2751. static int
  2752. TestReportCopyDirectory(src, dst, errorPtr)
  2753.     Tcl_Obj *src; /* Pathname of directory to be copied
  2754.  * (UTF-8). */
  2755.     Tcl_Obj *dst; /* Pathname of target directory (UTF-8). */
  2756.     Tcl_Obj **errorPtr;         /* If non-NULL, to be filled with UTF-8 name 
  2757.                                  * of file causing error. */
  2758. {
  2759.     TestReport("copydirectory",src,dst);
  2760.     return Tcl_FSCopyDirectory(TestReportGetNativePath(src), 
  2761.     TestReportGetNativePath(dst), errorPtr);
  2762. }
  2763. static int
  2764. TestReportRemoveDirectory(path, recursive, errorPtr)
  2765.     Tcl_Obj *path; /* Pathname of directory to be removed
  2766.  * (UTF-8). */
  2767.     int recursive; /* If non-zero, removes directories that
  2768.  * are nonempty.  Otherwise, will only remove
  2769.  * empty directories. */
  2770.     Tcl_Obj **errorPtr;         /* If non-NULL, to be filled with UTF-8 name 
  2771.                                  * of file causing error. */
  2772. {
  2773.     TestReport("removedirectory",path,NULL);
  2774.     return Tcl_FSRemoveDirectory(TestReportGetNativePath(path), recursive, 
  2775.  errorPtr);
  2776. }
  2777. static CONST char**
  2778. TestReportFileAttrStrings(fileName, objPtrRef)
  2779.     Tcl_Obj* fileName;
  2780.     Tcl_Obj** objPtrRef;
  2781. {
  2782.     TestReport("fileattributestrings",fileName,NULL);
  2783.     return Tcl_FSFileAttrStrings(TestReportGetNativePath(fileName), objPtrRef);
  2784. }
  2785. static int
  2786. TestReportFileAttrsGet(interp, index, fileName, objPtrRef)
  2787.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2788.     int index; /* index of the attribute command. */
  2789.     Tcl_Obj *fileName; /* filename we are operating on. */
  2790.     Tcl_Obj **objPtrRef; /* for output. */
  2791. {
  2792.     TestReport("fileattributesget",fileName,NULL);
  2793.     return Tcl_FSFileAttrsGet(interp, index, 
  2794.       TestReportGetNativePath(fileName), objPtrRef);
  2795. }
  2796. static int
  2797. TestReportFileAttrsSet(interp, index, fileName, objPtr)
  2798.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2799.     int index; /* index of the attribute command. */
  2800.     Tcl_Obj *fileName; /* filename we are operating on. */
  2801.     Tcl_Obj *objPtr; /* for input. */
  2802. {
  2803.     TestReport("fileattributesset",fileName,objPtr);
  2804.     return Tcl_FSFileAttrsSet(interp, index, 
  2805.       TestReportGetNativePath(fileName), objPtr);
  2806. }
  2807. static int 
  2808. TestReportUtime (fileName, tval)
  2809.     Tcl_Obj* fileName;
  2810.     struct utimbuf *tval;
  2811. {
  2812.     TestReport("utime",fileName,NULL);
  2813.     return Tcl_FSUtime(TestReportGetNativePath(fileName), tval);
  2814. }
  2815. static int
  2816. TestReportNormalizePath(interp, pathPtr, nextCheckpoint)
  2817.     Tcl_Interp *interp;
  2818.     Tcl_Obj *pathPtr;
  2819.     int nextCheckpoint;
  2820. {
  2821.     TestReport("normalizepath",pathPtr,NULL);
  2822.     return nextCheckpoint;
  2823. }
  2824. static int 
  2825. SimplePathInFilesystem(Tcl_Obj *pathPtr, ClientData *clientDataPtr) {
  2826.     CONST char *str = Tcl_GetString(pathPtr);
  2827.     if (strncmp(str,"simplefs:/",10)) {
  2828. return -1;
  2829.     }
  2830.     return TCL_OK;
  2831. }
  2832. /* 
  2833.  * Since TclCopyChannel insists on an interpreter, we use this
  2834.  * to simplify our test scripts.  Would be better if it could
  2835.  * copy without an interp
  2836.  */
  2837. static Tcl_Interp *simpleInterpPtr = NULL;
  2838. /* We use this to ensure we clean up after ourselves */
  2839. static Tcl_Obj *tempFile = NULL;
  2840. /* 
  2841.  * This is a very 'hacky' filesystem which is used just to 
  2842.  * test two important features of the vfs code: (1) that
  2843.  * you can load a shared library from a vfs, (2) that when
  2844.  * copying files from one fs to another, the 'mtime' is
  2845.  * preserved.
  2846.  * 
  2847.  * It treats any file in 'simplefs:/' as a file, and
  2848.  * artificially creates a real file on the fly which it uses
  2849.  * to extract information from.  The real file it uses is
  2850.  * whatever follows the trailing '/' (e.g. 'foo' in 'simplefs:/foo'),
  2851.  * and that file is assumed to exist in the native pwd, and is
  2852.  * copied over to the native temporary directory where it is
  2853.  * accessed.
  2854.  * 
  2855.  * Please do not consider this filesystem a model of how
  2856.  * things are to be done.  It is quite the opposite!  But, it
  2857.  * does allow us to test two important features.
  2858.  * 
  2859.  * Finally: this fs can only be used from one interpreter.
  2860.  */
  2861. static int
  2862. TestSimpleFilesystemObjCmd(dummy, interp, objc, objv)
  2863.     ClientData dummy;
  2864.     Tcl_Interp *interp;
  2865.     int objc;
  2866.     Tcl_Obj *CONST objv[];
  2867. {
  2868.     int res, boolVal;
  2869.     char *msg;
  2870.     
  2871.     if (objc != 2) {
  2872. Tcl_WrongNumArgs(interp, 1, objv, "boolean");
  2873. return TCL_ERROR;
  2874.     }
  2875.     if (Tcl_GetBooleanFromObj(interp, objv[1], &boolVal) != TCL_OK) {
  2876. return TCL_ERROR;
  2877.     }
  2878.     if (boolVal) {
  2879. res = Tcl_FSRegister((ClientData)interp, &simpleFilesystem);
  2880. msg = (res == TCL_OK) ? "registered" : "failed";
  2881. simpleInterpPtr = interp;
  2882.     } else {
  2883. if (tempFile != NULL) {
  2884.     Tcl_FSDeleteFile(tempFile);
  2885.     Tcl_DecrRefCount(tempFile);
  2886.     tempFile = NULL;
  2887. }
  2888. res = Tcl_FSUnregister(&simpleFilesystem);
  2889. msg = (res == TCL_OK) ? "unregistered" : "failed";
  2890. simpleInterpPtr = NULL;
  2891.     }
  2892.     Tcl_SetResult(interp, msg, TCL_VOLATILE);
  2893.     return res;
  2894. }
  2895. /* 
  2896.  * Treats a file name 'simplefs:/foo' by copying the file 'foo'
  2897.  * in the current (native) directory to a temporary native file,
  2898.  * and then returns that native file.
  2899.  */
  2900. static Tcl_Obj*
  2901. SimpleCopy(pathPtr)
  2902.     Tcl_Obj *pathPtr;                   /* Name of file to copy. */
  2903. {
  2904.     int res;
  2905.     CONST char *str;
  2906.     Tcl_Obj *origPtr;
  2907.     Tcl_Obj *tempPtr;
  2908.     tempPtr = TclpTempFileName();
  2909.     Tcl_IncrRefCount(tempPtr);
  2910.     /* 
  2911.      * We assume the same name in the current directory is ok.
  2912.      */
  2913.     str = Tcl_GetString(pathPtr);
  2914.     origPtr = Tcl_NewStringObj(str+10,-1);
  2915.     Tcl_IncrRefCount(origPtr);
  2916.     res = TclCrossFilesystemCopy(simpleInterpPtr, origPtr, tempPtr);
  2917.     Tcl_DecrRefCount(origPtr);
  2918.     if (res != TCL_OK) {
  2919. Tcl_FSDeleteFile(tempPtr);
  2920. Tcl_DecrRefCount(tempPtr);
  2921. return NULL;
  2922.     }
  2923.     return tempPtr;
  2924. }
  2925. static Tcl_Channel
  2926. SimpleOpenFileChannel(interp, pathPtr, mode, permissions)
  2927.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  2928.  * can be NULL. */
  2929.     Tcl_Obj *pathPtr;                   /* Name of file to open. */
  2930.     int mode;              /* POSIX open mode. */
  2931.     int permissions;                    /* If the open involves creating a
  2932.  * file, with what modes to create
  2933.  * it? */
  2934. {
  2935.     Tcl_Obj *tempPtr;
  2936.     Tcl_Channel chan;
  2937.     
  2938.     if ((mode != 0) && !(mode & O_RDONLY)) {
  2939. Tcl_AppendResult(interp, "read-only", 
  2940. (char *) NULL);
  2941. return NULL;
  2942.     }
  2943.     
  2944.     tempPtr = SimpleCopy(pathPtr);
  2945.     
  2946.     if (tempPtr == NULL) {
  2947. return NULL;
  2948.     }
  2949.     
  2950.     chan = Tcl_FSOpenFileChannel(interp, tempPtr, "r", permissions);
  2951.     if (tempFile != NULL) {
  2952.         Tcl_FSDeleteFile(tempFile);
  2953. Tcl_DecrRefCount(tempFile);
  2954. tempFile = NULL;
  2955.     }
  2956.     /* 
  2957.      * Store file pointer in this global variable so we can delete
  2958.      * it later 
  2959.      */
  2960.     tempFile = tempPtr;
  2961.     return chan;
  2962. }
  2963. static int
  2964. SimpleAccess(pathPtr, mode)
  2965.     Tcl_Obj *pathPtr; /* Path of file to access (in current CP). */
  2966.     int mode;                   /* Permission setting. */
  2967. {
  2968.     /* All files exist */
  2969.     return TCL_OK;
  2970. }
  2971. static int
  2972. SimpleStat(pathPtr, bufPtr)
  2973.     Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
  2974.     Tcl_StatBuf *bufPtr; /* Filled with results of stat call. */
  2975. {
  2976.     Tcl_Obj *tempPtr = SimpleCopy(pathPtr);
  2977.     if (tempPtr == NULL) {
  2978. /* We just pretend the file exists anyway */
  2979. return TCL_OK;
  2980.     } else {
  2981. int res = Tcl_FSStat(tempPtr, bufPtr);
  2982. Tcl_FSDeleteFile(tempPtr);
  2983. Tcl_DecrRefCount(tempPtr);
  2984. return res;
  2985.     }
  2986. }
  2987. static Tcl_Obj*
  2988. SimpleListVolumes(void)
  2989. {
  2990.     /* Add one new volume */
  2991.     Tcl_Obj *retVal;
  2992.     retVal = Tcl_NewStringObj("simplefs:/",-1);
  2993.     Tcl_IncrRefCount(retVal);
  2994.     return retVal;
  2995. }
  2996. /*
  2997.  * Used to check correct string-length determining in Tcl_NumUtfChars
  2998.  */
  2999. static int
  3000. TestNumUtfCharsCmd(clientData, interp, objc, objv)
  3001.     ClientData clientData;
  3002.     Tcl_Interp *interp;
  3003.     int objc;
  3004.     Tcl_Obj *CONST objv[];
  3005. {
  3006.     if (objc > 1) {
  3007. int len = -1;
  3008. if (objc > 2) {
  3009.     (void) Tcl_GetStringFromObj(objv[1], &len);
  3010. }
  3011. len = Tcl_NumUtfChars(Tcl_GetString(objv[1]), len);
  3012. Tcl_SetObjResult(interp, Tcl_NewIntObj(len));
  3013.     }
  3014.     return TCL_OK;
  3015. }