home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume26 / tclx / part22 < prev    next >
Encoding:
Text File  |  1991-11-19  |  44.5 KB  |  1,443 lines

  1. Newsgroups: comp.sources.misc
  2. From: karl@sugar.neosoft.com (Karl Lehenbauer)
  3. Subject:  v26i022:  tclx - extensions and on-line help for tcl 6.1, Part22/23
  4. Message-ID: <1991Nov19.135844.1688@sparky.imd.sterling.com>
  5. X-Md4-Signature: 7872693e9f39da42eb10b15a1d3182e3
  6. Date: Tue, 19 Nov 1991 13:58:44 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: karl@sugar.neosoft.com (Karl Lehenbauer)
  10. Posting-number: Volume 26, Issue 22
  11. Archive-name: tclx/part22
  12. Environment: UNIX
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then unpack
  16. # it by saving it into a file and typing "sh file".  To overwrite existing
  17. # files, type "sh file -c".  You can also feed this as standard input via
  18. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  19. # will see the following message at the end:
  20. #        "End of archive 22 (of 23)."
  21. # Contents:  extended/src/iocmds.c
  22. # Wrapped by karl@one on Wed Nov 13 21:50:33 1991
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. if test -f 'extended/src/iocmds.c' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'extended/src/iocmds.c'\"
  26. else
  27. echo shar: Extracting \"'extended/src/iocmds.c'\" \(41928 characters\)
  28. sed "s/^X//" >'extended/src/iocmds.c' <<'END_OF_FILE'
  29. X/*
  30. X * iocmds.c
  31. X *
  32. X * Extended Tcl file I/O commands.
  33. X *---------------------------------------------------------------------------
  34. X * Copyright 1991 Karl Lehenbauer and Mark Diekhans.
  35. X *
  36. X * Permission to use, copy, modify, and distribute this software and its
  37. X * documentation for any purpose and without fee is hereby granted, provided
  38. X * that the above copyright notice appear in all copies.  Karl Lehenbauer and
  39. X * Mark Diekhans make no representations about the suitability of this
  40. X * software for any purpose.  It is provided "as is" without express or
  41. X * implied warranty.
  42. X */
  43. X
  44. X#include "tclExtdInt.h"
  45. X#include <math.h>
  46. X
  47. X#ifdef TCL_USE_BZERO_MACRO
  48. X#    define bzero(to,length)    memset(to,'\0',length)
  49. X#endif
  50. X
  51. X/*
  52. X * Macro to enable line buffering mode on a file.
  53. X */
  54. X#ifdef TCL_HAVE_SETLINEBUF
  55. X#   define SET_LINE_BUF(fp)  setlinebuf (fp)
  56. X#else
  57. X#   define SET_LINE_BUF(fp)  setvbuf (fp, NULL, _IOLBF, BUFSIZ)
  58. X#endif
  59. X
  60. X
  61. X/*
  62. X * Control block used to pass data used by the binary search routines.
  63. X */
  64. Xtypedef struct binSearchCB_t {
  65. X    Tcl_Interp   *interp;         /* Pointer to the interpreter.             */
  66. X    char         *cmdName;        /* Cmd name to include in error msg.       */
  67. X    char         *fileHandle;     /* Handle of file.                         */
  68. X    char         *key;            /* The key to search for.                  */
  69. X
  70. X    FILE         *fileCBPtr;      /* Open file structure.                    */
  71. X    dynamicBuf_t  dynBuf;         /* Dynamic buffer to hold a line of file.  */
  72. X    long          lastRecOffset;  /* Offset of last record read.             */
  73. X    int           cmpResult;      /* -1, 0 or 1 result of string compare.    */
  74. X    char         *tclProc;        /* Name of Tcl comparsion proc, or NULL.   */
  75. X    } binSearchCB_t;
  76. X
  77. X/*
  78. X * Prototypes of internal functions.
  79. X */
  80. Xint
  81. XStandardKeyCompare _ANSI_ARGS_((char *key,
  82. X                                char *line));
  83. X
  84. Xint
  85. XTclProcKeyCompare _ANSI_ARGS_((binSearchCB_t *searchCBPtr));
  86. X
  87. Xint
  88. XReadAndCompare _ANSI_ARGS_((long           fileOffset,
  89. X                            binSearchCB_t *searchCBPtr));
  90. X
  91. Xint
  92. XBinSearch _ANSI_ARGS_((binSearchCB_t *searchCBPtr));
  93. X
  94. XFILE *
  95. XDoNormalDup _ANSI_ARGS_((Interp     *iPtr,
  96. X                         char       *tclCommand,
  97. X                         OpenFile   *oldFilePtr));
  98. X
  99. XFILE *
  100. XDoSpecialDup _ANSI_ARGS_((Interp     *iPtr,
  101. X                          char       *tclCommand,
  102. X                          OpenFile   *oldFilePtr,
  103. X                          char       *newHandleName));
  104. X
  105. Xint
  106. XGetFcntlFlags _ANSI_ARGS_((Tcl_Interp *interp,
  107. X                           char       *cmdName,
  108. X                           OpenFile   *filePtr));
  109. X
  110. Xint
  111. XSetFcntlFlag _ANSI_ARGS_((Tcl_Interp *interp,
  112. X                          char       *cmdName,
  113. X                          char       *flagName,
  114. X                          char       *valueStr,
  115. X                          OpenFile   *filePtr));
  116. X
  117. Xint
  118. XParseSelectFileList _ANSI_ARGS_((Tcl_Interp *interp,
  119. X                                 char       *handleList,
  120. X                                 fd_set     *fileDescSetPtr,
  121. X                                 int       **fileDescListPtr,
  122. X                                 int        *maxFileIdPtr));
  123. X
  124. Xstatic char *
  125. XReturnSelectedFileList _ANSI_ARGS_((fd_set     *fileDescSetPtr,
  126. X                                    int         fileDescCnt,
  127. X                                    int        *fileDescList));
  128. X
  129. X/*
  130. X *----------------------------------------------------------------------
  131. X *
  132. X * StandardKeyCompare --
  133. X *    Standard comparison routine for BinSearch, compares the key to the
  134. X *    first white-space seperated field in the line.
  135. X *
  136. X * Parameters:
  137. X *   o key (I) - The key to search for.
  138. X *   o line (I) - The line to compare the key to.
  139. X *
  140. X * Results:
  141. X *   o < 0 if key < line-key
  142. X *   o = 0 if key == line-key
  143. X *   o > 0 if key > line-key.
  144. X *----------------------------------------------------------------------
  145. X */
  146. Xstatic int
  147. XStandardKeyCompare (key, line)
  148. X    char *key;
  149. X    char *line;
  150. X{
  151. X    int  cmpResult, fieldLen;
  152. X    char saveChar;
  153. X
  154. X    fieldLen = strcspn (line, " \t\r\n\v\f");
  155. X
  156. X    saveChar = line [fieldLen];
  157. X    line [fieldLen] = 0;
  158. X    cmpResult = strcmp (key, line);
  159. X    line [fieldLen] = saveChar;
  160. X
  161. X    return cmpResult;
  162. X}
  163. X
  164. X/*
  165. X *----------------------------------------------------------------------
  166. X *
  167. X * TclProcKeyCompare --
  168. X *    Comparison routine for BinSearch that runs a Tcl procedure to, 
  169. X *    compare the key to a line from the file.
  170. X *
  171. X * Parameters:
  172. X *   o searchCBPtr (I/O) - The search control block, the line should be in
  173. X *     dynBuf, the comparsion result is returned in cmpResult.
  174. X *
  175. X * Results:
  176. X *   TCL_OK or TCL_ERROR.
  177. X *----------------------------------------------------------------------
  178. X */
  179. Xstatic int
  180. XTclProcKeyCompare (searchCBPtr)
  181. X    binSearchCB_t *searchCBPtr;
  182. X{
  183. X    char *cmdArgv [3];
  184. X    char *command;
  185. X    int   result;
  186. X
  187. X    cmdArgv [0] = searchCBPtr->tclProc;
  188. X    cmdArgv [1] = searchCBPtr->key;
  189. X    cmdArgv [2] = searchCBPtr->dynBuf.ptr;
  190. X    command = Tcl_Merge (3, cmdArgv);
  191. X
  192. X    result = Tcl_Eval (searchCBPtr->interp, command, 0, (char **) NULL);
  193. X
  194. X    ckfree (command);
  195. X    if (result == TCL_ERROR)
  196. X        return TCL_ERROR;
  197. X
  198. X    if (!Tcl_StrToInt (searchCBPtr->interp->result, 0, 
  199. X                       &searchCBPtr->cmpResult)) {
  200. X        char *oldResult = ckalloc (strlen (searchCBPtr->interp->result + 1));
  201. X        
  202. X        strcpy (oldResult, searchCBPtr->interp->result);
  203. X        Tcl_ResetResult (searchCBPtr->interp);
  204. X        Tcl_AppendResult (searchCBPtr->interp, "invalid integer \"", oldResult,
  205. X                          "\" returned from compare proc \"",
  206. X                          searchCBPtr->tclProc, "\"", (char *) NULL);
  207. X        ckfree (oldResult);
  208. X        return TCL_ERROR;
  209. X    }
  210. X    Tcl_ResetResult (searchCBPtr->interp);
  211. X    return TCL_OK;
  212. X}
  213. X
  214. X/*
  215. X *----------------------------------------------------------------------
  216. X *
  217. X * ReadAndCompare --
  218. X *    Search for the next line in the file starting at the specified
  219. X *    offset.  Read the line into the dynamic buffer and compare it to
  220. X *    the key using the specified comparison method.  The start of the
  221. X *    last line read is saved in the control block, and if the start of
  222. X *    the same line is found in the search, then it will not be recompared.
  223. X *    This is needed since the search algorithm has to hit the same line
  224. X *    a couple of times before failing, due to the fact that the records are
  225. X *    not fixed length.
  226. X *
  227. X * Parameters:
  228. X *   o fileOffset (I) - The offset of the next byte of the search, not
  229. X *     necessarly the start of a record.
  230. X *   o searchCBPtr (I/O) - The search control block, the comparsion result
  231. X *     is returned in cmpResult.  If the EOF is hit, a less-than result is
  232. X *     returned.
  233. X *
  234. X * Results:
  235. X *   TCL_OK or TCL_ERROR.
  236. X *----------------------------------------------------------------------
  237. X */
  238. Xstatic int
  239. XReadAndCompare (fileOffset, searchCBPtr)
  240. X    long           fileOffset;
  241. X    binSearchCB_t *searchCBPtr;
  242. X{
  243. X    int  recChar, status;
  244. X
  245. X    if (fseek (searchCBPtr->fileCBPtr, fileOffset, SEEK_SET) != 0)
  246. X        goto unixError;
  247. X
  248. X    /*
  249. X     * Go to beginning of next line.
  250. X     */
  251. X    
  252. X    if (fileOffset != 0) {
  253. X        while (((recChar = getc (searchCBPtr->fileCBPtr)) != EOF) &&
  254. X                (recChar != '\n'))
  255. X            fileOffset++;
  256. X        if ((recChar == EOF) && ferror (searchCBPtr->fileCBPtr))
  257. X            goto unixError;
  258. X    }
  259. X    /*
  260. X     * If this is the same line as before, then just leave the comparison
  261. X     * result unchanged.
  262. X     */
  263. X    if (fileOffset == searchCBPtr->lastRecOffset)
  264. X        return TCL_OK;
  265. X
  266. X    searchCBPtr->lastRecOffset = fileOffset;
  267. X
  268. X    status = Tcl_DynamicFgets (&searchCBPtr->dynBuf, searchCBPtr->fileCBPtr);
  269. X    if (status < 0)
  270. X        goto unixError;
  271. X
  272. X    /* 
  273. X     * Only compare if EOF was not hit, otherwise, treat as if we went
  274. X     * above the key we are looking for.
  275. X     */
  276. X    if (status == 0) {
  277. X        searchCBPtr->cmpResult = -1;
  278. X        return TCL_OK;
  279. X    }
  280. X
  281. X    if (searchCBPtr->tclProc == NULL) {
  282. X        searchCBPtr->cmpResult = StandardKeyCompare (searchCBPtr->key, 
  283. X                                                     searchCBPtr->dynBuf.ptr);
  284. X    } else {
  285. X        if (TclProcKeyCompare (searchCBPtr) != TCL_OK)
  286. X            return TCL_ERROR;
  287. X    }
  288. X
  289. X    return TCL_OK;
  290. X
  291. XunixError:
  292. X   Tcl_AppendResult (searchCBPtr->interp, searchCBPtr->cmdName, 
  293. X                     ": ", searchCBPtr->fileHandle, ": ",
  294. X                     Tcl_UnixError (searchCBPtr->interp), (char *) NULL);
  295. X   return TCL_ERROR;
  296. X}
  297. X
  298. X/*
  299. X *----------------------------------------------------------------------
  300. X *
  301. X * BinSearch --
  302. X *      Binary search a sorted ASCII file.
  303. X *
  304. X * Parameters:
  305. X *   o searchCBPtr (I/O) - The search control block, if the line is found,
  306. X *     it is returned in dynBuf.
  307. X * Results:
  308. X *     TCL_OK - If the key was found.
  309. X *     TCL_BREAK - If it was not found.
  310. X *     TCL_ERROR - If there was an error.
  311. X *
  312. X * based on getpath.c from smail 2.5 (9/15/87)
  313. X *
  314. X *----------------------------------------------------------------------
  315. X */
  316. Xstatic int
  317. XBinSearch (searchCBPtr)
  318. X    binSearchCB_t *searchCBPtr;
  319. X{
  320. X    OpenFile   *filePtr;
  321. X    long        middle, high, low;
  322. X    struct stat statBuf;
  323. X
  324. X    if (TclGetOpenFile (searchCBPtr->interp, searchCBPtr->fileHandle, 
  325. X                        &filePtr) != TCL_OK)
  326. X        goto unixError;
  327. X
  328. X    searchCBPtr->fileCBPtr = filePtr->f;
  329. X    searchCBPtr->lastRecOffset = -1;
  330. X
  331. X    if (fstat (fileno (searchCBPtr->fileCBPtr), &statBuf) < 0)
  332. X        goto unixError;
  333. X
  334. X    low = 0;
  335. X    high = statBuf.st_size;
  336. X
  337. X    /*
  338. X     * "Binary search routines are never written right the first time around."
  339. X     * - Robert G. Sheldon.
  340. X     */
  341. X
  342. X    while (TRUE) {
  343. X        middle = (high + low + 1) / 2;
  344. X
  345. X        if (ReadAndCompare (middle, searchCBPtr) != TCL_OK)
  346. X            return TCL_ERROR;
  347. X
  348. X        if (searchCBPtr->cmpResult == 0)
  349. X            return TCL_OK;     /* Found   */
  350. X        
  351. X        if (low >= middle)  
  352. X            return TCL_BREAK;  /* Failure */
  353. X
  354. X        /*
  355. X         * Close window.
  356. X         */
  357. X        if (searchCBPtr->cmpResult > 0) {
  358. X            low = middle;
  359. X        } else {
  360. X            high = middle - 1;
  361. X        }
  362. X    }
  363. X
  364. XunixError:
  365. X   Tcl_AppendResult (searchCBPtr->interp, searchCBPtr->cmdName, 
  366. X                     ": ", searchCBPtr->fileHandle, ": ",
  367. X                     Tcl_UnixError (searchCBPtr->interp), (char *) NULL);
  368. X   return TCL_ERROR;
  369. X}
  370. X
  371. X/*
  372. X *----------------------------------------------------------------------
  373. X *
  374. X * Tcl_BsearchCmd --
  375. X *     Implements the TCL bsearch command:
  376. X *        bsearch filehandle key [retvar]
  377. X *
  378. X * Results:
  379. X *      Standard TCL results.
  380. X *
  381. X *----------------------------------------------------------------------
  382. X */
  383. Xint
  384. XTcl_BsearchCmd (clientData, interp, argc, argv)
  385. X    ClientData  clientData;
  386. X    Tcl_Interp *interp;
  387. X    int         argc;
  388. X    char      **argv;
  389. X{
  390. X    int           status;
  391. X    binSearchCB_t searchCB;
  392. X
  393. X    if ((argc < 3) || (argc > 5)) {
  394. X        Tcl_AppendResult (interp, "wrong # args: ", argv [0], 
  395. X                          " handle key [retvar] [compare_proc]"
  396. X                          , (char *) NULL);
  397. X        return TCL_ERROR;
  398. X    }
  399. X
  400. X    searchCB.interp = interp;
  401. X    searchCB.cmdName = argv [0];
  402. X    searchCB.fileHandle = argv [1];
  403. X    searchCB.key = argv [2];
  404. X    searchCB.tclProc = (argc == 5) ? argv [4] : NULL;
  405. X    Tcl_DynBufInit (&searchCB.dynBuf);
  406. X
  407. X    status = BinSearch (&searchCB);
  408. X    if (status == TCL_ERROR) {
  409. X        Tcl_DynBufFree (&searchCB.dynBuf);
  410. X        return TCL_ERROR;
  411. X    }
  412. X
  413. X    if (status == TCL_BREAK) {
  414. X        Tcl_DynBufFree (&searchCB.dynBuf);
  415. X        if ((argc >= 4) && (argv [3][0] != '\0'))
  416. X            interp->result = "0";
  417. X        return TCL_OK;
  418. X    }
  419. X
  420. X    if ((argc == 3) || (argv [3][0] == '\0')) {
  421. X        Tcl_DynBufReturn (interp, &searchCB.dynBuf);
  422. X    } else {
  423. X        char *varPtr;
  424. X
  425. X        varPtr = Tcl_SetVar (interp, argv[3], searchCB.dynBuf.ptr,
  426. X                             TCL_LEAVE_ERR_MSG);
  427. X        Tcl_DynBufFree (&searchCB.dynBuf);
  428. X        if (varPtr == NULL)
  429. X            return TCL_ERROR;
  430. X        interp->result = "1";
  431. X    }
  432. X    return TCL_OK;
  433. X}
  434. X
  435. X/*
  436. X *----------------------------------------------------------------------
  437. X *
  438. X * DoNormalDup --
  439. X *   Process a normal dup command (i.e. the new file is not specified).
  440. X *
  441. X * Parameters:
  442. X *   o iPtr (I) - If an error occures, the error message is in result,
  443. X *     otherwise the file handle is in result.
  444. X *   o tclCommand (I) - The command name (argv [0]), for error reporting.
  445. X *   o oldFilePtr (I) - Tcl file control block for the file to dup.
  446. X * Returns:
  447. X *   A pointer to the FILE structure for the new file, or NULL if an
  448. X *   error occured. 
  449. X *----------------------------------------------------------------------
  450. X */
  451. Xstatic FILE *
  452. XDoNormalDup (iPtr, tclCommand, oldFilePtr)
  453. X    Interp     *iPtr;
  454. X    char       *tclCommand;
  455. X    OpenFile   *oldFilePtr;
  456. X{
  457. X    int       newFileId;
  458. X    FILE     *newFileCbPtr;
  459. X    char     *mode;
  460. X
  461. X    newFileId = dup (fileno (oldFilePtr->f));
  462. X    if (newFileId < 0)
  463. X        goto unixError;
  464. X
  465. X    TclMakeFileTable (iPtr, newFileId);
  466. X    if (iPtr->filePtrArray [newFileId] != NULL) {
  467. X        panic ("Tcl_OpenCmd found file already open");
  468. X    }
  469. X    /*
  470. X     * Set up a stdio FILE control block for the new file.
  471. X     */
  472. X    if (oldFilePtr->readable && oldFilePtr->writable) {
  473. X        mode = "r+";
  474. X    } else if (oldFilePtr->writable) {
  475. X        mode = "w";
  476. X    } else {
  477. X        mode = "r";
  478. X    }
  479. X    if ((newFileCbPtr = fdopen (newFileId, mode)) == NULL)
  480. X        goto unixError;
  481. X
  482. X    sprintf (iPtr->result, "file%d", newFileId);
  483. X    return newFileCbPtr;
  484. X
  485. XunixError:
  486. X    Tcl_AppendResult ((Tcl_Interp *) iPtr, tclCommand, ": ", 
  487. X                      Tcl_UnixError ((Tcl_Interp *) iPtr), (char *) NULL);
  488. X    return NULL;
  489. X}
  490. X
  491. X/*
  492. X *----------------------------------------------------------------------
  493. X *
  494. X * DoSpecialDup --
  495. X *   Process a special dup command.  This is the case were the file is
  496. X *   dup-ed to stdin, stdout or stderr.  The new file may or be open or
  497. X *   closed
  498. X * Parameters:
  499. X *   o iPtr (I) - If an error occures, the error message is in result,
  500. X *     otherwise nothing is returned.
  501. X *   o tclCommand (I) - The command name (argv [0]), for error reporting.
  502. X *   o oldFilePtr (I) - Tcl file control block for the file to dup.
  503. X *   o newFileHandle (I) - The handle name for the new file.
  504. X * Returns:
  505. X *   A pointer to the FILE structure for the new file, or NULL if an
  506. X *   error occured. 
  507. X *----------------------------------------------------------------------
  508. X */
  509. Xstatic FILE *
  510. XDoSpecialDup (iPtr, tclCommand, oldFilePtr, newHandleName)
  511. X    Interp     *iPtr;
  512. X    char       *tclCommand;
  513. X    OpenFile   *oldFilePtr;
  514. X    char       *newHandleName;
  515. X{
  516. X    int       newFileId;
  517. X    FILE     *newFileCbPtr;
  518. X
  519. X    /*
  520. X     * Duplicate the old file to the specified file id.
  521. X     */
  522. X    newFileId = Tcl_ConvertFileHandle ((Tcl_Interp *) iPtr, newHandleName);
  523. X    if (newFileId < 0)
  524. X        return NULL;
  525. X    if (newFileId > 2) {
  526. X        Tcl_AppendResult (iPtr, "target handle must be one of stdin, ",
  527. X                          "stdout, stderr, file0, file1, or file2: got \"",
  528. X                          newHandleName, "\"", (char *) NULL);
  529. X        return NULL;
  530. X    }
  531. X    switch (newFileId) {
  532. X        case 0: 
  533. X            newFileCbPtr = stdin;
  534. X            break;
  535. X        case 1: 
  536. X            newFileCbPtr = stdout;
  537. X            break;
  538. X        case 2: 
  539. X            newFileCbPtr = stderr;
  540. X            break;
  541. X    }
  542. X
  543. X    /*
  544. X     * If the specified id is not open, set up a stdio file descriptor.
  545. X     */
  546. X    TclMakeFileTable (iPtr, newFileId);
  547. X    if (iPtr->filePtrArray [newFileId] == NULL) {
  548. X        char *mode;
  549. X
  550. X        /*
  551. X         * Set up a stdio FILE control block for the new file.
  552. X         */
  553. X        if (oldFilePtr->readable && oldFilePtr->writable) {
  554. X            mode = "r+";
  555. X        } else if (oldFilePtr->writable) {
  556. X            mode = "w";
  557. X        } else {
  558. X            mode = "r";
  559. X        }
  560. X        if (freopen ("/dev/null", mode, newFileCbPtr) == NULL)
  561. X            goto unixError;
  562. X    }
  563. X    
  564. X    /*
  565. X     * This functionallity may be obtained with dup2 on most systems.  Being
  566. X     * open is optional.
  567. X     */
  568. X    close (newFileId);
  569. X    if (fcntl (fileno (oldFilePtr->f), F_DUPFD, newFileId) < 0)
  570. X        goto unixError;
  571. X
  572. X    return newFileCbPtr;
  573. X
  574. XunixError:
  575. X    Tcl_AppendResult ((Tcl_Interp *) iPtr, tclCommand, ": ", 
  576. X                      Tcl_UnixError ((Tcl_Interp *) iPtr), (char *) NULL);
  577. X    return NULL;
  578. X}
  579. X
  580. X/*
  581. X *----------------------------------------------------------------------
  582. X *
  583. X * Tcl_DupCmd --
  584. X *     Implements the dup TCL command:
  585. X *         dup filehandle [stdhandle]
  586. X *
  587. X * Results:
  588. X *      Returns TCL_OK and interp->result containing a filehandle
  589. X *      if the requested file or pipe was successfully duplicated.
  590. X *
  591. X *      Return TCL_ERROR and interp->result containing an
  592. X *      explanation of what went wrong if an error occured.
  593. X *
  594. X * Side effects:
  595. X *      Locates and creates an entry in the handles table
  596. X *
  597. X *----------------------------------------------------------------------
  598. X */
  599. Xint
  600. XTcl_DupCmd (clientData, interp, argc, argv)
  601. X    ClientData  clientData;
  602. X    Tcl_Interp *interp;
  603. X    int         argc;
  604. X    char      **argv;
  605. X{
  606. X    Interp   *iPtr = (Interp *) interp;
  607. X    OpenFile *oldFilePtr;
  608. X    FILE     *newFileCbPtr;
  609. X    OpenFile *newFilePtr;
  610. X    long      seekOffset = -1;
  611. X
  612. X    if ((argc < 2) || (argc > 3)) {
  613. X        Tcl_AppendResult (interp, "wrong # arg: ", argv[0], 
  614. X                          " filehandle [stdhandle]", (char *) NULL);
  615. X        return TCL_ERROR;
  616. X    }
  617. X
  618. X    if (TclGetOpenFile(interp, argv[1], &oldFilePtr) != TCL_OK)
  619. X    return TCL_ERROR;
  620. X    if (oldFilePtr->numPids > 0) { /*??????*/
  621. X        Tcl_AppendResult (interp, "can not `dup' a pipeline", (char *) NULL);
  622. X        return TCL_ERROR;
  623. X    }
  624. X
  625. X    /*
  626. X     * If writable, flush out the buffer.  If readable, remember were we are
  627. X     * so the we can set it up for the next stdio read to come from the same
  628. X     * place.  The location is only recorded if the file is a reqular file,
  629. X     * since you cann't seek on other types of files.
  630. X     */
  631. X    if (oldFilePtr->writable) {
  632. X        if (fflush (oldFilePtr->f) != 0)
  633. X            goto unixError;
  634. X    }
  635. X    if (oldFilePtr->readable) {
  636. X        struct stat statBuf;
  637. X        
  638. X        if (fstat (fileno (oldFilePtr->f), &statBuf) < 0)
  639. X            goto unixError;
  640. X        if ((statBuf.st_mode & S_IFMT) == S_IFREG) {
  641. X            seekOffset = ftell (oldFilePtr->f);
  642. X            if (seekOffset < 0)
  643. X                goto unixError;
  644. X        }
  645. X    }
  646. X    /*
  647. X     * Process the dup depending on if dup-ing to a new file or a target
  648. X     * file handle.
  649. X     */
  650. X    if (argc == 2)
  651. X        newFileCbPtr = DoNormalDup (iPtr, argv [0], oldFilePtr);
  652. X    else
  653. X        newFileCbPtr = DoSpecialDup (iPtr, argv [0], oldFilePtr, argv [2]);
  654. X
  655. X    if (newFileCbPtr == NULL)
  656. X        return TCL_ERROR;
  657. X
  658. X    /*
  659. X     * Set up a Tcl OpenFile structure for the new file handle.
  660. X     */
  661. X    newFilePtr = iPtr->filePtrArray [fileno (newFileCbPtr)];
  662. X    if (newFilePtr == NULL) {
  663. X        newFilePtr = (OpenFile*) ckalloc (sizeof (OpenFile));
  664. X        iPtr->filePtrArray [fileno (newFileCbPtr)] = newFilePtr;
  665. X    }
  666. X    newFilePtr->f = newFileCbPtr;
  667. X    newFilePtr->f2 = NULL;
  668. X    newFilePtr->readable = oldFilePtr->readable;
  669. X    newFilePtr->writable = oldFilePtr->writable;
  670. X    newFilePtr->numPids = 0;
  671. X    newFilePtr->pidPtr = NULL;
  672. X    newFilePtr->errorId = -1;
  673. X
  674. X    if (seekOffset >= 0) {
  675. X        if (fseek (newFilePtr->f, seekOffset, SEEK_SET) != 0)
  676. X            goto unixError;
  677. X    }
  678. X    return TCL_OK;
  679. X
  680. XunixError:
  681. X    Tcl_AppendResult (interp, argv [0], ": ", Tcl_UnixError (interp), 
  682. X                      (char *) NULL);
  683. X    return TCL_ERROR;
  684. X}
  685. X
  686. X/*
  687. X *----------------------------------------------------------------------
  688. X *
  689. X * Tcl_PipeCmd --
  690. X *     Implements the pipe TCL command:
  691. X *         pipe [handle_var_r handle_var_w]
  692. X *
  693. X * Results:
  694. X *      Standard TCL result.
  695. X *
  696. X * Side effects:
  697. X *      Locates and creates entries in the handles table
  698. X *
  699. X *----------------------------------------------------------------------
  700. X */
  701. Xint
  702. XTcl_PipeCmd (clientData, interp, argc, argv)
  703. X    ClientData  clientData;
  704. X    Tcl_Interp *interp;
  705. X    int         argc;
  706. X    char      **argv;
  707. X{
  708. X    Interp    *iPtr = (Interp *) interp;
  709. X    FILE      *file0CbPtr, *file1CbPtr;
  710. X    OpenFile  *file0Ptr,   *file1Ptr;
  711. X    int        fileIds [2];
  712. X    char       fHandle [12];
  713. X
  714. X    if (!((argc == 1) || (argc == 3))) {
  715. X        Tcl_AppendResult (interp, "wrong # args: ", argv[0], 
  716. X                          " [handle_var_r handle_var_w]", (char*) NULL);
  717. X    }
  718. X
  719. X    if (pipe (fileIds) < 0)
  720. X        goto unixError;
  721. X
  722. X    if (((file0CbPtr = fdopen (fileIds[0], "r")) == NULL) ||
  723. X            ((file1CbPtr = fdopen (fileIds[1], "w")) == NULL)) {
  724. X        close (fileIds [0]);
  725. X        close (fileIds [1]);
  726. X        goto unixError;
  727. X    }
  728. X
  729. X    TclMakeFileTable (iPtr,
  730. X                      (fileIds [0] > fileIds [1]) ? fileIds [0] : fileIds [1]);
  731. X    file0Ptr = (OpenFile*) ckalloc (sizeof (OpenFile));
  732. X    file0Ptr->f = file0CbPtr;
  733. X    file0Ptr->f2 = NULL;
  734. X    file0Ptr->readable = TRUE;
  735. X    file0Ptr->writable = FALSE;
  736. X    file0Ptr->numPids = 0;
  737. X    file0Ptr->pidPtr = NULL;
  738. X    file0Ptr->errorId = -1;
  739. X    iPtr->filePtrArray[fileIds [0]] = file0Ptr;
  740. X
  741. X    file1Ptr = (OpenFile*) ckalloc (sizeof (OpenFile));
  742. X    file1Ptr->f = file1CbPtr;
  743. X    file1Ptr->f2 = NULL;
  744. X    file1Ptr->readable = FALSE;
  745. X    file1Ptr->writable = TRUE;
  746. X    file1Ptr->numPids = 0;
  747. X    file1Ptr->pidPtr = NULL;
  748. X    file1Ptr->errorId = -1;
  749. X    iPtr->filePtrArray[fileIds [1]] = file1Ptr;
  750. X
  751. X
  752. X    if (argc == 1)      
  753. X        sprintf (interp->result, "file%d file%d", fileIds [0], fileIds [1]);
  754. X    else {
  755. X        sprintf (fHandle, "file%d", fileIds [0]);
  756. X        if (Tcl_SetVar (interp, argv[1], fHandle, TCL_LEAVE_ERR_MSG) == NULL)
  757. X            return TCL_ERROR;
  758. X
  759. X        sprintf (fHandle, "file%d", fileIds [1]);
  760. X        if (Tcl_SetVar (interp, argv[2], fHandle, TCL_LEAVE_ERR_MSG) == NULL)
  761. X            return TCL_ERROR;
  762. X    }
  763. X        
  764. X    return TCL_OK;
  765. X
  766. XunixError:
  767. X    Tcl_AppendResult (interp, argv [0], ": ", Tcl_UnixError (interp), 
  768. X                      (char *) NULL);
  769. X    return TCL_ERROR;
  770. X}
  771. X
  772. X/*
  773. X *----------------------------------------------------------------------
  774. X *
  775. X * Tcl_CopyfileCmd --
  776. X *     Implements the copyfile TCL command:
  777. X *         copyfile handle1 handle2 [lines]
  778. X *
  779. X * Results:
  780. X *      Nothing if it worked, else an error.
  781. X *
  782. X *----------------------------------------------------------------------
  783. X */
  784. Xint
  785. XTcl_CopyfileCmd (clientData, interp, argc, argv)
  786. X    ClientData  clientData;
  787. X    Tcl_Interp *interp;
  788. X    int         argc;
  789. X    char      **argv;
  790. X{
  791. X    OpenFile  *fromFilePtr, *toFilePtr;
  792. X    char       transferBuffer [2048];
  793. X    int        bytesRead;
  794. X
  795. X    if (argc != 3) {
  796. X        Tcl_AppendResult (interp, "wrong # args: ", argv [0], 
  797. X                          " fromfilehandle tofilehandle", (char *) NULL);
  798. X        return TCL_ERROR;
  799. X    }
  800. X
  801. X    if (TclGetOpenFile (interp, argv[1], &fromFilePtr) != TCL_OK)
  802. X    return TCL_ERROR;
  803. X    if (TclGetOpenFile (interp, argv[2], &toFilePtr) != TCL_OK)
  804. X    return TCL_ERROR;
  805. X
  806. X    if (!fromFilePtr->readable) {
  807. X        interp->result = "Source file is not open for read access";
  808. X    return TCL_ERROR;
  809. X    }
  810. X    if (!toFilePtr->writable) {
  811. X        interp->result = "Target file is not open for write access";
  812. X    return TCL_ERROR;
  813. X    }
  814. X
  815. X    while (TRUE) {
  816. X        bytesRead = fread (transferBuffer, sizeof (char), 
  817. X                           sizeof (transferBuffer), fromFilePtr->f);
  818. X        if (bytesRead <= 0) {
  819. X            if (feof (fromFilePtr->f))
  820. X                break;
  821. X            else
  822. X                goto unixError;
  823. X        }
  824. X        if (fwrite (transferBuffer, sizeof (char), bytesRead, toFilePtr->f) != 
  825. X                    bytesRead)
  826. X            goto unixError;
  827. X    }
  828. X
  829. X    return TCL_OK;
  830. X
  831. XunixError:
  832. X    Tcl_AppendResult (interp, argv [0], ": ", Tcl_UnixError (interp), 
  833. X                      (char *) NULL);
  834. X    return TCL_ERROR;
  835. X}
  836. X
  837. X/*
  838. X *----------------------------------------------------------------------
  839. X *
  840. X * Tcl_FstatCmd --
  841. X *     Implements the fstat TCL command:
  842. X *         fstat handle [arrayvar]
  843. X *----------------------------------------------------------------------
  844. X */
  845. Xint
  846. XTcl_FstatCmd (clientData, interp, argc, argv)
  847. X    ClientData  clientData;
  848. X    Tcl_Interp *interp;
  849. X    int         argc;
  850. X    char      **argv;
  851. X{
  852. X    OpenFile    *filePtr;
  853. X    struct stat  statBuf;
  854. X
  855. X    if ((argc < 2) || (argc > 3)) {
  856. X        Tcl_AppendResult (interp, "wrong # args: ", argv [0], 
  857. X                          " handle [arrayVar]", (char *) NULL);
  858. X        return TCL_ERROR;
  859. X    }
  860. X
  861. X    if (TclGetOpenFile (interp, argv[1], &filePtr) != TCL_OK)
  862. X    return TCL_ERROR;
  863. X    
  864. X    if (fstat (fileno (filePtr->f), &statBuf)) {
  865. X        Tcl_AppendResult (interp, argv [0], ": ", Tcl_UnixError (interp), 
  866. X                          (char *) NULL);
  867. X        return TCL_ERROR;
  868. X    }
  869. X    /*
  870. X     * Either return the arguments in an array or a list of keyword & value
  871. X     * elements.
  872. X     */
  873. X    if (argc == 2) {
  874. X        char statList [160];
  875. X
  876. X        sprintf (statList, 
  877. X                 "{atime %d} {ctime %d} {dev %d} {gid %d} {ino %d} ",
  878. X                  statBuf.st_atime, statBuf.st_ctime, statBuf.st_dev,
  879. X                  statBuf.st_gid,   statBuf.st_ino);
  880. X        Tcl_AppendResult (interp, statList, (char *) NULL);
  881. X
  882. X        sprintf (statList, 
  883. X                 "{mode %d} {mtime %d} {nlink %d} {size %d} {uid %d}",
  884. X                 statBuf.st_mode, statBuf.st_mtime, statBuf.st_nlink, 
  885. X                 statBuf.st_size, statBuf.st_uid);
  886. X        Tcl_AppendResult (interp, statList, (char *) NULL);
  887. X        
  888. X    } else {
  889. X        char numBuf [30];
  890. X
  891. X    sprintf (numBuf, "%d", statBuf.st_dev);
  892. X    if  (Tcl_SetVar2 (interp, argv[2], "dev", numBuf, 
  893. X                          TCL_LEAVE_ERR_MSG) == NULL)
  894. X        return TCL_ERROR;
  895. X
  896. X    sprintf (numBuf, "%d", statBuf.st_ino);
  897. X    if  (Tcl_SetVar2 (interp, argv[2], "ino", numBuf,
  898. X                             TCL_LEAVE_ERR_MSG) == NULL)
  899. X        return TCL_ERROR;
  900. X
  901. X    sprintf (numBuf, "%d", statBuf.st_mode);
  902. X    if  (Tcl_SetVar2 (interp, argv[2], "mode", numBuf, 
  903. X                          TCL_LEAVE_ERR_MSG) == NULL)
  904. X        return TCL_ERROR;
  905. X
  906. X    sprintf (numBuf, "%d", statBuf.st_nlink);
  907. X    if  (Tcl_SetVar2 (interp, argv[2], "nlink", numBuf,
  908. X                          TCL_LEAVE_ERR_MSG) == NULL)
  909. X        return TCL_ERROR;
  910. X
  911. X    sprintf (numBuf, "%d", statBuf.st_uid);
  912. X    if  (Tcl_SetVar2 (interp, argv[2], "uid", numBuf,
  913. X                          TCL_LEAVE_ERR_MSG) == NULL)
  914. X        return TCL_ERROR;
  915. X
  916. X    sprintf (numBuf, "%d", statBuf.st_gid);
  917. X    if  (Tcl_SetVar2 (interp, argv[2], "gid", numBuf,
  918. X                          TCL_LEAVE_ERR_MSG) == NULL)
  919. X        return TCL_ERROR;
  920. X
  921. X    sprintf (numBuf, "%d", statBuf.st_size);
  922. X    if  (Tcl_SetVar2 (interp, argv[2], "size", numBuf,
  923. X                          TCL_LEAVE_ERR_MSG) == NULL)
  924. X        return TCL_ERROR;
  925. X
  926. X    sprintf (numBuf, "%d", statBuf.st_atime);
  927. X    if  (Tcl_SetVar2 (interp, argv[2], "atime", numBuf,
  928. X                          TCL_LEAVE_ERR_MSG) == NULL)
  929. X        return TCL_ERROR;
  930. X
  931. X    sprintf (numBuf, "%d", statBuf.st_mtime);
  932. X    if  (Tcl_SetVar2 (interp, argv[2], "mtime", numBuf,
  933. X                          TCL_LEAVE_ERR_MSG) == NULL)
  934. X        return TCL_ERROR;
  935. X
  936. X    sprintf (numBuf, "%d", statBuf.st_ctime);
  937. X    if  (Tcl_SetVar2 (interp, argv[2], "ctime", numBuf,
  938. X                          TCL_LEAVE_ERR_MSG) == NULL)
  939. X        return TCL_ERROR;
  940. X
  941. X    }
  942. X    return TCL_OK;
  943. X
  944. X}
  945. X
  946. X/*
  947. X *----------------------------------------------------------------------
  948. X *
  949. X * GetFcntlFlags --
  950. X *    Return the fcntl values as a symbolic list in the result.
  951. X * Result:
  952. X *   Returns TCL_OK if all is well, TCL_ERROR if fcntl returns an error.
  953. X *----------------------------------------------------------------------
  954. X */
  955. Xstatic int
  956. XGetFcntlFlags (interp, cmdName, filePtr)
  957. X    Tcl_Interp *interp;
  958. X    char       *cmdName;
  959. X    OpenFile   *filePtr;
  960. X{
  961. X    int   flags;
  962. X    int   listArgc = 0;
  963. X    char *listArgv [9];
  964. X
  965. X    flags = fcntl (fileno (filePtr->f), F_GETFL, 0);
  966. X    if (flags == -1)
  967. X        goto unixError;
  968. X
  969. X    if (flags & O_RDONLY)
  970. X        listArgv [listArgc++] = "RDONLY";
  971. X    if (flags & O_WRONLY)
  972. X        listArgv [listArgc++] = "WRONLY";
  973. X    if (flags & O_RDWR)
  974. X        listArgv [listArgc++] = "RDWR";
  975. X    if (flags & O_NDELAY)
  976. X        listArgv [listArgc++] = "NDELAY";
  977. X    if (flags & O_APPEND)
  978. X        listArgv [listArgc++] = "APPEND";
  979. X
  980. X    flags = fcntl (fileno (filePtr->f), F_GETFD, 0);
  981. X    if (flags == -1)
  982. X        goto unixError;
  983. X    if (flags & 1) 
  984. X        listArgv [listArgc++] = "CLEXEC";
  985. X
  986. X    /*
  987. X     * Poke the stdio FILE structure to see if its buffered.
  988. X     */
  989. X
  990. X#ifdef _IONBF
  991. X    if (filePtr->f->_flag & _IONBF)
  992. X        listArgv [listArgc++] = "NOBUF";
  993. X    if (filePtr->f->_flag & _IOLBF)
  994. X        listArgv [listArgc++] = "LINEBUF";
  995. X#else
  996. X    if (filePtr->f->_flags & _SNBF)
  997. X        listArgv [listArgc++] = "NOBUF";
  998. X    if (filePtr->f->_flags & _SLBF)
  999. X        listArgv [listArgc++] = "LINEBUF";
  1000. X#endif
  1001. X
  1002. X    Tcl_SetResult (interp, Tcl_Merge (listArgc, listArgv), TCL_DYNAMIC);
  1003. X    return TCL_OK;
  1004. X
  1005. XunixError:
  1006. X    Tcl_AppendResult (interp, cmdName, ": ", Tcl_UnixError (interp), 
  1007. X                      (char *) NULL);
  1008. X    return TCL_ERROR;
  1009. X   
  1010. X}
  1011. X
  1012. X/*
  1013. X *----------------------------------------------------------------------
  1014. X *
  1015. X * SetFcntlFlag --
  1016. X *    Set the specified fcntl flag to the given boolean value.
  1017. X * Result:
  1018. X *   Returns TCL_OK if all is well, TCL_ERROR if there is an error.
  1019. X *----------------------------------------------------------------------
  1020. X */
  1021. Xstatic int
  1022. XSetFcntlFlag (interp, cmdName, flagName, valueStr, filePtr)
  1023. X    Tcl_Interp *interp;
  1024. X    char       *cmdName;
  1025. X    char       *flagName;
  1026. X    char       *valueStr;
  1027. X    OpenFile   *filePtr;
  1028. X{
  1029. X#define   MAX_FLAG_NAME_LEN  12
  1030. X#define   CLEXEC_FLAG   1
  1031. X#define   NOBUF_FLAG    2
  1032. X#define   LINEBUF_FLAG  4
  1033. X
  1034. X    int   setFlag = 0, otherFlag = 0, setValue;
  1035. X    char  flagNameUp [MAX_FLAG_NAME_LEN + 1];
  1036. X    
  1037. X    if (Tcl_GetBoolean (interp, valueStr, &setValue) != TCL_OK)
  1038. X        return TCL_ERROR;
  1039. X
  1040. X    if (strlen (flagName) > MAX_FLAG_NAME_LEN)
  1041. X        goto invalidFlagName;
  1042. X    Tcl_UpShift (flagNameUp, flagName);
  1043. X
  1044. X    if (STREQU (flagNameUp, "NDELAY"))
  1045. X        setFlag = O_NDELAY;
  1046. X    else if (STREQU (flagNameUp, "APPEND"))
  1047. X        setFlag = O_APPEND;
  1048. X    else if (STREQU (flagNameUp, "CLEXEC"))
  1049. X        otherFlag = CLEXEC_FLAG;
  1050. X    else if (STREQU (flagNameUp, "NOBUF"))
  1051. X        otherFlag = NOBUF_FLAG;
  1052. X    else if (STREQU (flagNameUp, "LINEBUF"))
  1053. X        otherFlag = LINEBUF_FLAG;
  1054. X    else {
  1055. X        Tcl_AppendResult (interp, "unknown attribute name \"", flagName,
  1056. X                          "\", expected one of: APPEND, CLEXEC, NDELAY, ",
  1057. X                          "NOBUF, LINEBUF", (char *) NULL);
  1058. X        return TCL_ERROR;
  1059. X    }
  1060. X
  1061. X    if (otherFlag == CLEXEC_FLAG) {
  1062. X        if (fcntl (fileno (filePtr->f), F_SETFD, setValue) == -1)
  1063. X            goto unixError;
  1064. X    } else if (otherFlag != 0) {
  1065. X        if (setValue != 1) {
  1066. X            Tcl_AppendResult (interp, flagNameUp, " flag may not be cleared",
  1067. X                              (char *) NULL);
  1068. X            return TCL_ERROR;
  1069. X        }
  1070. X        if (otherFlag == NOBUF_FLAG)
  1071. X            setbuf (filePtr->f, NULL);
  1072. X        else
  1073. X            SET_LINE_BUF (filePtr->f);
  1074. X    } else {
  1075. X        int flags;
  1076. X
  1077. X        flags = fcntl (fileno (filePtr->f), F_GETFL, 0);
  1078. X        if (flags == -1)
  1079. X            goto unixError;
  1080. X        flags = flags & ~setFlag;
  1081. X        if (setValue)
  1082. X            flags = flags | setFlag;
  1083. X        if (fcntl (fileno (filePtr->f), F_SETFL, flags) == -1)
  1084. X            goto unixError;
  1085. X    }
  1086. X    return TCL_OK;
  1087. X
  1088. XinvalidFlagName:
  1089. X    Tcl_AppendResult (interp, cmdName, ": invalid flag name \"", flagName,
  1090. X                      "\", expected one of: NDELAY, APPEND, CLEXEC",
  1091. X                      (char *) NULL);
  1092. X    return TCL_ERROR;
  1093. XunixError:
  1094. X    Tcl_AppendResult (interp, cmdName, ": ", Tcl_UnixError (interp), 
  1095. X                      (char *) NULL);
  1096. X    return TCL_ERROR;
  1097. X   
  1098. X}
  1099. X
  1100. X/*
  1101. X *----------------------------------------------------------------------
  1102. X *
  1103. X * Tcl_FcntlCmd --
  1104. X *     Implements the fcntl TCL command:
  1105. X *         fcntl handle [attribute value]
  1106. X *----------------------------------------------------------------------
  1107. X */
  1108. Xint
  1109. XTcl_FcntlCmd (clientData, interp, argc, argv)
  1110. X    ClientData  clientData;
  1111. X    Tcl_Interp *interp;
  1112. X    int         argc;
  1113. X    char      **argv;
  1114. X{
  1115. X    OpenFile    *filePtr;
  1116. X
  1117. X    if (!((argc == 2) || (argc == 4))) {
  1118. X        Tcl_AppendResult (interp, "wrong # args: ", argv [0], 
  1119. X                          " handle [attribute value]", (char *) NULL);
  1120. X        return TCL_ERROR;
  1121. X    }
  1122. X
  1123. X    if (TclGetOpenFile (interp, argv[1], &filePtr) != TCL_OK)
  1124. X    return TCL_ERROR;
  1125. X    if (argc == 2) {    
  1126. X        if (GetFcntlFlags (interp, argv [0], filePtr) != TCL_OK)
  1127. X            return TCL_ERROR;
  1128. X    } else {
  1129. X        if (SetFcntlFlag (interp, argv [0], argv [2], argv [3], 
  1130. X                          filePtr) != TCL_OK)
  1131. X            return TCL_ERROR;
  1132. X    }
  1133. X    return TCL_OK;
  1134. X}
  1135. X#ifndef TCL_NO_SELECT
  1136. X
  1137. X/*
  1138. X *----------------------------------------------------------------------
  1139. X *
  1140. X * ParseSelectFileList --
  1141. X *
  1142. X *   Parse a list of file handles for select.
  1143. X *
  1144. X * Parameters:
  1145. X *   o interp (O) - Error messages are returned in the result.
  1146. X *   o handleList (I) - The list of file handles to parse, may be empty.
  1147. X *   o fileDescSetPtr (O) - The select fd_set for the parsed handles is
  1148. X *     filled in.  Should be cleared before this procedure is called.
  1149. X *   o fileDescListPtr (O) - A pointer to a dynamically allocated list of
  1150. X *     the integer file ids that are in the set.  If the list is empty,
  1151. X *     NULL is returned.
  1152. X *   o maxFileIdPtr (I/O) - If a file id greater than the current value is
  1153. X *     encountered, it will be set to that file id.
  1154. X * Returns:
  1155. X *   The number of files in the list, or -1 if an error occured.
  1156. X *----------------------------------------------------------------------
  1157. X */
  1158. Xstatic int
  1159. XParseSelectFileList (interp, handleList, fileDescSetPtr, fileDescListPtr,
  1160. X                     maxFileIdPtr)
  1161. X    Tcl_Interp *interp;
  1162. X    char       *handleList;
  1163. X    fd_set     *fileDescSetPtr;
  1164. X    int       **fileDescListPtr;
  1165. X    int        *maxFileIdPtr;
  1166. X{
  1167. X    int    handleCnt, idx;
  1168. X    char **handleArgv;
  1169. X    int   *fileDescList;
  1170. X
  1171. X    if (Tcl_SplitList (interp, handleList, &handleCnt, &handleArgv) != TCL_OK)
  1172. X        return -1;
  1173. X
  1174. X    /*
  1175. X     * Handle case of an empty list.
  1176. X     */
  1177. X    if (handleCnt == 0) {
  1178. X        *fileDescListPtr = NULL;
  1179. X        ckfree ((char *) handleArgv);
  1180. X        return 0;
  1181. X    }
  1182. X
  1183. X    fileDescList = (int *) ckalloc (sizeof (int) * handleCnt);
  1184. X
  1185. X    for (idx = 0; idx < handleCnt; idx++) {
  1186. X        OpenFile *filePtr;
  1187. X        int       fileId;
  1188. X
  1189. X        if (TclGetOpenFile (interp, handleArgv [idx], &filePtr) != TCL_OK) {
  1190. X            ckfree ((char *) handleArgv);
  1191. X            ckfree ((char *) fileDescList);
  1192. X            return -1;
  1193. X        }
  1194. X        fileId = fileno (filePtr->f);
  1195. X        fileDescList [idx] = fileId;
  1196. X
  1197. X        FD_SET (fileId, fileDescSetPtr);
  1198. X        if (fileId > *maxFileIdPtr)
  1199. X            *maxFileIdPtr = fileId;
  1200. X    }
  1201. X
  1202. X    *fileDescListPtr = fileDescList;
  1203. X    ckfree ((char *) handleArgv);
  1204. X    return handleCnt;
  1205. X}
  1206. X
  1207. X/*
  1208. X *----------------------------------------------------------------------
  1209. X *
  1210. X * ReturnSelectedFileList --
  1211. X *
  1212. X *   Take the resulting file descriptor sets from a select, and the
  1213. X *   list of file descritpors and build up a list of Tcl file handles.
  1214. X *
  1215. X * Parameters:
  1216. X *   o fileDescSetPtr (I) - The select fd_set.
  1217. X *   o fileDescCnt (I) - Number of descriptors in the list.
  1218. X *   o fileDescListPtr (I) - A pointer to a list of the integer file
  1219. X *     ids that are in the set.  If the list is empty,
  1220. X *     NULL is returned.
  1221. X * Returns:
  1222. X *   A dynamicly allocated list of file handles.  If the handles are empty,
  1223. X *   it still returns a NULL list to make clean up easy.
  1224. X *----------------------------------------------------------------------
  1225. X */
  1226. Xstatic char *
  1227. XReturnSelectedFileList (fileDescSetPtr, fileDescCnt, fileDescList) 
  1228. X    fd_set     *fileDescSetPtr;
  1229. X    int         fileDescCnt;
  1230. X    int        *fileDescList;
  1231. X{
  1232. X    int    idx, handleCnt;
  1233. X    char  *fileHandleList;
  1234. X    char **fileHandleArgv;
  1235. X
  1236. X    /*
  1237. X     * Special case the empty list.
  1238. X     */
  1239. X    if (fileDescCnt == 0) {
  1240. X        fileHandleList = ckalloc (1);
  1241. X        fileHandleList [0] = '\0';
  1242. X        return fileHandleList;
  1243. X    }
  1244. X
  1245. X    handleCnt = 0;
  1246. X    fileHandleArgv = (char **) ckalloc (sizeof (char *) * fileDescCnt);
  1247. X
  1248. X    for (idx = 0; idx < fileDescCnt; idx++) {
  1249. X        if (FD_ISSET (fileDescList [idx], fileDescSetPtr)) {
  1250. X            fileHandleArgv [handleCnt] = ckalloc (8);  /* fileNNN */
  1251. X            sprintf (fileHandleArgv [handleCnt], "file%d", fileDescList [idx]);
  1252. X            handleCnt++;
  1253. X        }
  1254. X    }
  1255. X
  1256. X    fileHandleList = Tcl_Merge (handleCnt, fileHandleArgv);
  1257. X    for (idx = 0; idx < handleCnt; idx++)
  1258. X        ckfree ((char *) fileHandleArgv [idx]);
  1259. X    ckfree ((char *) fileHandleArgv);
  1260. X
  1261. X    return fileHandleList;
  1262. X}
  1263. X
  1264. X/*
  1265. X *----------------------------------------------------------------------
  1266. X *
  1267. X * Tcl_SelectCmd --
  1268. X *     Implements the select TCL command:
  1269. X *          select readhandles [writehandles] [excepthandles] [timeout]
  1270. X *
  1271. X * Results:
  1272. X *     A list in the form:
  1273. X *        {readhandles writehandles excepthandles}
  1274. X *     or {} it the timeout expired.
  1275. X *----------------------------------------------------------------------
  1276. X */
  1277. Xint
  1278. XTcl_SelectCmd (clientData, interp, argc, argv)
  1279. X    ClientData  clientData;
  1280. X    Tcl_Interp *interp;
  1281. X    int         argc;
  1282. X    char      **argv;
  1283. X{
  1284. X
  1285. X    fd_set readFdSet,            writeFdSet,            exceptFdSet;
  1286. X    int    readDescCnt = 0,      writeDescCnt = 0,      exceptDescCnt = 0;
  1287. X    int   *readDescList = NULL, *writeDescList = NULL, *exceptDescList = NULL;
  1288. X    char  *retListArgv [3];
  1289. X
  1290. X    int             numSelected, maxFileId = 0;
  1291. X    int             result = TCL_ERROR;
  1292. X    struct timeval  timeoutRec;
  1293. X    struct timeval *timeoutRecPtr;
  1294. X
  1295. X
  1296. X    if (argc < 2) {
  1297. X        Tcl_AppendResult (interp, "wrong # args: ", argv [0], 
  1298. X                          " readhandles [writehandles] [excepthandles]",
  1299. X                          " [timeout]", (char *) NULL);
  1300. X        return TCL_ERROR;
  1301. X    }
  1302. X    
  1303. X    /*
  1304. X     * Parse the file handles and set everything up for the select call.
  1305. X     */
  1306. X    FD_ZERO (&readFdSet);
  1307. X    FD_ZERO (&writeFdSet);
  1308. X    FD_ZERO (&exceptFdSet);
  1309. X    readDescCnt = ParseSelectFileList (interp, argv [1], &readFdSet, 
  1310. X                                       &readDescList, &maxFileId);
  1311. X    if (readDescCnt < 0)
  1312. X        goto exitPoint;
  1313. X    if (argc > 2) {
  1314. X        writeDescCnt = ParseSelectFileList (interp, argv [2], &writeFdSet, 
  1315. X                                            &writeDescList, &maxFileId);
  1316. X        if (writeDescCnt < 0)
  1317. X            goto exitPoint;
  1318. X    }
  1319. X    if (argc > 3) {
  1320. X        exceptDescCnt = ParseSelectFileList (interp, argv [3], &exceptFdSet, 
  1321. X                                             &exceptDescList, &maxFileId);
  1322. X        if (exceptDescCnt < 0)
  1323. X            goto exitPoint;
  1324. X    }
  1325. X    
  1326. X    /*
  1327. X     * Get the time out.  Zero is different that not specified.
  1328. X     */
  1329. X    timeoutRecPtr = NULL;
  1330. X    if ((argc > 4) && (argv [4][0] != '\0')) {
  1331. X        double  timeout, seconds, microseconds;
  1332. X
  1333. X        if (Tcl_GetDouble (interp, argv [4], &timeout) != TCL_OK)
  1334. X            goto exitPoint;
  1335. X        if (timeout < 0) {
  1336. X            Tcl_AppendResult (interp, "timeout must be greater than or equal",
  1337. X                              " to zero", (char *) NULL);
  1338. X            goto exitPoint;
  1339. X        }
  1340. X        seconds = floor (timeout);
  1341. X        microseconds = (timeout - seconds) * 1000000.0;
  1342. X        timeoutRec.tv_sec = seconds;
  1343. X        timeoutRec.tv_usec = microseconds;
  1344. X        timeoutRecPtr = &timeoutRec;
  1345. X    }
  1346. X
  1347. X    /*
  1348. X     * All set, do the select.
  1349. X     */
  1350. X    numSelected = select (maxFileId + 1, &readFdSet, &writeFdSet, &exceptFdSet,
  1351. X                          timeoutRecPtr);
  1352. X    if (numSelected < 0) {
  1353. X        Tcl_AppendResult (interp, argv [0], ": system call error:", 
  1354. X                          Tcl_UnixError (interp), (char *) NULL);
  1355. X        goto exitPoint;
  1356. X    }
  1357. X
  1358. X    /*
  1359. X     * Return the result, either a 3 element list, or leave the result
  1360. X     * empty if the timeout occured.
  1361. X     */
  1362. X    if (numSelected > 0) {
  1363. X        retListArgv [0] = ReturnSelectedFileList (&readFdSet, readDescCnt,
  1364. X                                                  readDescList);
  1365. X        retListArgv [1] = ReturnSelectedFileList (&writeFdSet, writeDescCnt,
  1366. X                                                  writeDescList);
  1367. X        retListArgv [2] = ReturnSelectedFileList (&exceptFdSet, exceptDescCnt,
  1368. X                                                  exceptDescList);
  1369. X        Tcl_SetResult (interp, Tcl_Merge (3, retListArgv), TCL_DYNAMIC); 
  1370. X        ckfree ((char *) retListArgv [0]);
  1371. X        ckfree ((char *) retListArgv [1]);
  1372. X        ckfree ((char *) retListArgv [2]);
  1373. X    }
  1374. X
  1375. X    result = TCL_OK;
  1376. X
  1377. XexitPoint:
  1378. X    if (readDescList != NULL)
  1379. X        ckfree ((char *) readDescList);
  1380. X    if (writeDescList != NULL)
  1381. X        ckfree ((char *) writeDescList);
  1382. X    if (exceptDescList != NULL)
  1383. X        ckfree ((char *) exceptDescList);
  1384. X    return result;
  1385. X
  1386. XunixError:
  1387. X    return TCL_ERROR;
  1388. X}
  1389. X#else
  1390. X/*
  1391. X *----------------------------------------------------------------------
  1392. X *
  1393. X * Tcl_SelectCmd --
  1394. X *     Dummy select command that returns an error for systems that don't
  1395. X *     have select.
  1396. X *----------------------------------------------------------------------
  1397. X */
  1398. Xint
  1399. XTcl_SelectCmd (clientData, interp, argc, argv)
  1400. X    ClientData  clientData;
  1401. X    Tcl_Interp *interp;
  1402. X    int         argc;
  1403. X    char      **argv;
  1404. X{
  1405. X    Tcl_AppendResult (interp, 
  1406. X                      "select is not available on this version of Unix",
  1407. X                      (char *) NULL);
  1408. X    return TCL_ERROR;
  1409. X}
  1410. X#endif
  1411. END_OF_FILE
  1412. if test 41928 -ne `wc -c <'extended/src/iocmds.c'`; then
  1413.     echo shar: \"'extended/src/iocmds.c'\" unpacked with wrong size!
  1414. fi
  1415. # end of 'extended/src/iocmds.c'
  1416. fi
  1417. echo shar: End of archive 22 \(of 23\).
  1418. cp /dev/null ark22isdone
  1419. MISSING=""
  1420. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ; do
  1421.     if test ! -f ark${I}isdone ; then
  1422.     MISSING="${MISSING} ${I}"
  1423.     fi
  1424. done
  1425. if test "${MISSING}" = "" ; then
  1426.     echo You have unpacked all 23 archives.
  1427.     echo "Now cd to "extended", edit the makefile, then do a "make""
  1428.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1429. else
  1430.     echo You still need to unpack the following archives:
  1431.     echo "        " ${MISSING}
  1432. fi
  1433. ##  End of shell archive.
  1434. exit 0
  1435.  
  1436. exit 0 # Just in case...
  1437. -- 
  1438. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1439. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1440. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1441. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1442.