home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume27 / clc / part17 < prev    next >
Encoding:
Text File  |  1993-11-28  |  30.4 KB  |  1,180 lines

  1. Newsgroups: comp.sources.unix
  2. From: panos@anchor.cs.colorado.edu (Panos Tsirigotis)
  3. Subject: v27i123: clc - C Libraries Collection, Part17/20
  4. References: <1.754527080.23891@gw.home.vix.com>
  5. Sender: unix-sources-moderator@gw.home.vix.com
  6. Approved: vixie@gw.home.vix.com
  7.  
  8. Submitted-By: panos@anchor.cs.colorado.edu (Panos Tsirigotis)
  9. Posting-Number: Volume 27, Issue 123
  10. Archive-Name: clc/part17
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then unpack
  14. # it by saving it into a file and typing "sh file".  To overwrite existing
  15. # files, type "sh file -c".  You can also feed this as standard input via
  16. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  17. # will see the following message at the end:
  18. #        "End of archive 17 (of 20)."
  19. # Contents:  libs/src/dict/bst.c libs/src/sio/sio.3
  20. # Wrapped by panos@eclipse on Sun Nov 28 14:48:17 1993
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'libs/src/dict/bst.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'libs/src/dict/bst.c'\"
  24. else
  25. echo shar: Extracting \"'libs/src/dict/bst.c'\" \(14392 characters\)
  26. sed "s/^X//" >'libs/src/dict/bst.c' <<'END_OF_FILE'
  27. X/*
  28. X * (c) Copyright 1993 by Panagiotis Tsirigotis
  29. X * All rights reserved.  The file named COPYRIGHT specifies the terms 
  30. X * and conditions for redistribution.
  31. X */
  32. X
  33. Xstatic char RCSid[] = "$Id: bst.c,v 3.3 93/09/28 21:06:50 panos Exp $" ;
  34. X
  35. X#include "bstimpl.h"
  36. X
  37. X
  38. X#define NODE_ALLOC( hp )               TNP( fsm_alloc( (hp)->alloc ) )
  39. X#define NODE_FREE( hp, np )            fsm_free( (hp)->alloc, (char *)(np) )
  40. X
  41. X
  42. X/*
  43. X * Find the minimum node of the subtree with root 'start'
  44. X */
  45. X#define FIND_MINIMUM( hp, start, x )                                                    \
  46. X                                    x = start ;                                                    \
  47. X                                    while ( LEFT( x ) != NIL( hp ) ) x = LEFT( x )
  48. X
  49. X/*
  50. X * Find the maximum node of the subtree with root 'start'
  51. X */
  52. X#define FIND_MAXIMUM( hp, start, x )                                                    \
  53. X                                    x = start ;                                                    \
  54. X                                    while ( RIGHT( x ) != NIL( hp ) ) x = RIGHT( x )
  55. X
  56. X
  57. X/*
  58. X * Returns a pointer to the node that contains the specified object
  59. X * or NIL( hp ) if the object is not found
  60. X */
  61. XPRIVATE tnode_s *find_object( hp, object )
  62. X    header_s *hp ;
  63. X    dict_obj object ;
  64. X{
  65. X    dheader_s             *dhp     = DHP( hp ) ;
  66. X    register tnode_s    *np    = ROOT( hp ) ;
  67. X    register tnode_s    *null = NIL( hp ) ;
  68. X    register int         v ;
  69. X
  70. X    while ( np != null )
  71. X    {
  72. X        v = (*dhp->oo_comp)( object, OBJ( np ) ) ;
  73. X        if ( v == 0 )
  74. X            if ( object == OBJ( np ) )
  75. X                break ;
  76. X            else
  77. X                v = -1 ;
  78. X        np = ( v < 0 ) ? LEFT( np ) : RIGHT( np ) ;
  79. X    }
  80. X    return( np ) ;
  81. X}
  82. X
  83. X
  84. X
  85. X/*
  86. X * Create a tree (either simple or red-black)
  87. X */
  88. Xdict_h bst_create( oo_comp, ko_comp, flags, errnop )
  89. X    dict_function    oo_comp ;                    /* object-object comparator */
  90. X    dict_function    ko_comp ;                    /* key-object comparator */
  91. X    int                 flags ;
  92. X    int                *errnop ;
  93. X{
  94. X    register header_s    *hp ;
  95. X    unsigned                tnode_size ;
  96. X    bool_int             balanced_tree    = flags & DICT_BALANCED_TREE ;
  97. X    char                    *id = "bst_create" ;
  98. X
  99. X    if ( ! __dict_args_ok( id, flags, errnop, oo_comp, ko_comp, DICT_ORDERED ) )
  100. X        return( NULL_HANDLE ) ;
  101. X
  102. X    hp = THP( malloc( sizeof( header_s ) ) ) ;
  103. X    if ( hp == NULL_HEADER )
  104. X        return( __dict_create_error( id, flags, errnop, DICT_ENOMEM ) ) ;
  105. X    
  106. X    /*
  107. X     * Create an allocator
  108. X     */
  109. X    tnode_size = sizeof( struct tree_node ) ;
  110. X    if ( balanced_tree )
  111. X        tnode_size = sizeof( struct balanced_tree_node ) ;
  112. X
  113. X    hp->alloc = fsm_create( tnode_size, 0,
  114. X            ( flags & DICT_RETURN_ERROR ) ? FSM_RETURN_ERROR : FSM_NOFLAGS ) ;
  115. X    if ( hp->alloc == NULL )
  116. X    {
  117. X        free( (char *)hp ) ;
  118. X        return( __dict_create_error( id, flags, errnop, DICT_ENOMEM ) ) ;
  119. X    }
  120. X
  121. X    LEFT( ANCHOR( hp ) ) = RIGHT( ANCHOR( hp ) ) = NIL( hp ) ;
  122. X    OBJ( ANCHOR( hp ) ) = NULL_OBJ ;
  123. X    OBJ( NIL( hp ) ) = NULL_OBJ ;
  124. X
  125. X    if ( balanced_tree )
  126. X    {
  127. X        COLOR( ANCHOR( hp ) ) = BLACK ;
  128. X        COLOR( NIL( hp ) ) = BLACK ;
  129. X    }
  130. X    
  131. X    /*
  132. X     * Initialize dictionary header, hints
  133. X     */
  134. X    __dict_init_header( DHP( hp ), oo_comp, ko_comp, flags, errnop ) ;
  135. X    HINT_CLEAR( hp, last_search ) ;
  136. X    HINT_CLEAR( hp, last_successor ) ;
  137. X    HINT_CLEAR( hp, last_predecessor ) ;
  138. X
  139. X    return( (dict_h) hp ) ;
  140. X}
  141. X
  142. X
  143. X/*
  144. X * Destroy a tree
  145. X */
  146. Xvoid bst_destroy( handle )
  147. X    dict_h handle ;
  148. X{
  149. X    header_s *hp = THP( handle ) ;
  150. X
  151. X    fsm_destroy( hp->alloc ) ;
  152. X    free( (char *)hp ) ;
  153. X}
  154. X
  155. X
  156. X
  157. X/*
  158. X * Common code for tree insertions
  159. X */
  160. XPRIVATE int tree_insert( hp, uniq, object, objectp )
  161. X    header_s            *hp ;
  162. X    register int    uniq ;
  163. X    dict_obj            object ;
  164. X    dict_obj            *objectp ;
  165. X{
  166. X    dheader_s            *dhp    = DHP( hp ) ;
  167. X    register tnode_s    *x        = ROOT( hp ) ;
  168. X    register tnode_s    *px    = ANCHOR( hp ) ;
  169. X    tnode_s                *new ;
  170. X    register int         v ;
  171. X
  172. X    if ( object == NULL_OBJ )
  173. X        HANDLE_ERROR( dhp, "tree_insert", DICT_ENULLOBJECT, DICT_ERR ) ;
  174. X
  175. X    /*
  176. X     * On exit from this loop, 'px' will point to a leaf node
  177. X     * Furthermore, 'v' will hold the comparison value between the
  178. X     * object stored at this node and the new 'object'.
  179. X     *
  180. X     * v must be initialized to -1 so that when we insert a node into an 
  181. X     * empty tree that node will be the *left* child of the anchor node
  182. X     */
  183. X    v = -1 ;
  184. X    while ( x != NIL( hp ) )
  185. X    {
  186. X        v = (*dhp->oo_comp)( object, OBJ( x ) ) ;
  187. X
  188. X        if ( v == 0 )
  189. X            if ( uniq )
  190. X            {
  191. X                if ( objectp != NULL )
  192. X                    *objectp = OBJ( x ) ;
  193. X                ERRNO( dhp ) = DICT_EEXISTS ;
  194. X                return( DICT_ERR ) ;
  195. X            }
  196. X            else
  197. X                v = -1 ;            /* i.e. LESS THAN */
  198. X        
  199. X        px = x ;
  200. X        x = ( v < 0 ) ? LEFT( x ) : RIGHT( x ) ;
  201. X    }
  202. X
  203. X    /*
  204. X     * Allocate a new node and initialize all its fields
  205. X     */
  206. X    new = NODE_ALLOC( hp ) ;
  207. X    if ( new == NULL_NODE )
  208. X    {
  209. X        ERRNO( dhp ) = DICT_ENOMEM ;
  210. X        return( DICT_ERR ) ;
  211. X    }
  212. X    LEFT( new ) = RIGHT( new ) = NIL( hp ) ;
  213. X    OBJ( new ) = object ;
  214. X    PARENT( new ) = px ;
  215. X
  216. X    if ( v < 0 )
  217. X        LEFT( px ) = new ;
  218. X    else
  219. X        RIGHT( px ) = new ;
  220. X
  221. X    if ( dhp->flags & DICT_BALANCED_TREE )
  222. X        __dict_rbt_insfix( hp, new ) ;
  223. X    
  224. X    if ( objectp != NULL )
  225. X        *objectp = object ;
  226. X
  227. X    return( DICT_OK ) ;
  228. X}
  229. X
  230. X
  231. X/*
  232. X * Insert the specified object in the tree
  233. X */
  234. Xint bst_insert( handle, object )
  235. X    dict_h        handle ;
  236. X    dict_obj        object ;
  237. X{
  238. X    header_s *hp = THP( handle ) ;
  239. X
  240. X    return( tree_insert( hp,
  241. X        hp->dh.flags & DICT_UNIQUE_KEYS, object, (dict_obj *)NULL ) ) ;
  242. X}
  243. X
  244. X
  245. X/*
  246. X * Insert the specified object in the tree only if it is unique
  247. X */
  248. Xint bst_insert_uniq( handle, object, objectp )
  249. X    dict_h        handle ;
  250. X    dict_obj     object ;
  251. X    dict_obj     *objectp ;
  252. X{
  253. X    return( tree_insert( THP( handle ), TRUE, object, objectp ) ) ;
  254. X}
  255. X
  256. X
  257. X/*
  258. X * Delete the specified object
  259. X */
  260. Xint bst_delete( handle, object )
  261. X    dict_h         handle ;
  262. X    dict_obj     object ;
  263. X{
  264. X    header_s                *hp    = THP( handle ) ;
  265. X    dheader_s            *dhp    = DHP( hp ) ;
  266. X    register tnode_s    *null = NIL( hp ) ;
  267. X    tnode_s                *delnp ;
  268. X    register tnode_s    *y, *x ;
  269. X    tnode_s                *py ;
  270. X
  271. X    if ( object == NULL_OBJ )
  272. X        HANDLE_ERROR( dhp, "bst_delete", DICT_ENULLOBJECT, DICT_ERR ) ;
  273. X
  274. X    if ( HINT_MATCH( hp, last_search, object ) )
  275. X        delnp = HINT_GET( hp, last_search ) ;
  276. X    else
  277. X    {
  278. X        delnp = find_object( hp, object ) ;
  279. X        if ( delnp == null )
  280. X        {
  281. X            ERRNO( dhp ) = DICT_ENOTFOUND ;
  282. X            return( DICT_ERR ) ;
  283. X        }
  284. X    }
  285. X
  286. X    HINT_CLEAR( hp, last_search ) ;
  287. X    HINT_CLEAR( hp, last_successor ) ;
  288. X    HINT_CLEAR( hp, last_predecessor ) ;
  289. X
  290. X    /*
  291. X     * y is the node actually being removed. It may be 'delnp' if
  292. X     * 'delnp' has at most 1 child, otherwise it is 'delnp's successor
  293. X     */
  294. X    if ( LEFT( delnp ) == null || RIGHT( delnp ) == null )
  295. X        y = delnp ;
  296. X    else
  297. X    {
  298. X        /*
  299. X         * We can use the FIND_MINIMUM macro since we are guaranteed
  300. X         * there is a right child
  301. X         */
  302. X        FIND_MINIMUM( hp, RIGHT( delnp ), y ) ;
  303. X        OBJ( delnp ) = OBJ( y ) ;
  304. X    }
  305. X
  306. X    /*
  307. X     * Set x to one of y's children:
  308. X     *        Case 1: y == delnp
  309. X     *            pick any child
  310. X     *        Case 2: y == successor( delnp )
  311. X     *            y can only have a right child (if any), so pick that.
  312. X     * Next, we link x to y's parent.
  313. X     *
  314. X     * The use of the NIL and ANCHOR nodes relieves us from checking
  315. X     * boundary conditions:
  316. X     *        existence of x    (when setting the PARENT field of x)
  317. X     *        y being the root of the tree
  318. X     */
  319. X    x = ( LEFT( y ) != null ) ? LEFT( y ) : RIGHT( y ) ;
  320. X    py = PARENT( y ) ;
  321. X    PARENT( x ) = py ;
  322. X    if ( y == LEFT( py ) )
  323. X        LEFT( py ) = x ;
  324. X    else
  325. X        RIGHT( py ) = x ;
  326. X
  327. X    /*
  328. X     * If this is a balanced tree and we unbalanced it, do the necessary repairs
  329. X     */
  330. X    if ( ( dhp->flags & DICT_BALANCED_TREE ) && COLOR( y ) == BLACK )
  331. X        __dict_rbt_delfix( hp, x ) ;
  332. X
  333. X    NODE_FREE( hp, y ) ;
  334. X
  335. X    return( DICT_OK ) ;
  336. X}
  337. X
  338. X
  339. X/*
  340. X * Find an object with the specified key
  341. X */
  342. Xdict_obj bst_search( handle, key )
  343. X    dict_h        handle ;
  344. X    dict_key        key ;
  345. X{
  346. X    header_s                *hp    = THP( handle ) ;
  347. X    dheader_s            *dhp    = DHP( hp ) ;
  348. X    register tnode_s    *np    = ROOT( hp ) ;
  349. X    register tnode_s    *null = NIL( hp ) ;
  350. X    register int         v ;
  351. X
  352. X    while ( np != null )
  353. X    {
  354. X        v = (*dhp->ko_comp)( key, OBJ( np ) ) ;
  355. X        if ( v == 0 )
  356. X            break ;
  357. X        else
  358. X            np = ( v < 0 ) ? LEFT( np ) : RIGHT( np ) ;
  359. X    }
  360. X    HINT_SET( hp, last_search, np ) ;         /* update search hint */
  361. X    return( OBJ( np ) ) ;
  362. X}
  363. X
  364. X
  365. X/*
  366. X * Returns a pointer to the object with the smallest key value or
  367. X * NULL_OBJ if the tree is empty.
  368. X */
  369. Xdict_obj bst_minimum( handle )
  370. X    dict_h        handle ;
  371. X{
  372. X    register header_s        *hp = THP( handle ) ;
  373. X    register tnode_s        *np ;
  374. X
  375. X    if ( TREE_EMPTY( hp ) )
  376. X        return( NULL_OBJ ) ;
  377. X    FIND_MINIMUM( hp, ROOT( hp ), np ) ;
  378. X    HINT_SET( hp, last_successor, np ) ;
  379. X    return( OBJ( np ) ) ;
  380. X}
  381. X
  382. X
  383. X/*
  384. X * Returns a pointer to the object with the greatest key value or
  385. X * NULL_OBJ if the tree is empty.
  386. X */
  387. Xdict_obj bst_maximum( handle )
  388. X    dict_h        handle ;
  389. X{
  390. X    register header_s        *hp = THP( handle ) ;
  391. X    register tnode_s        *np ;
  392. X
  393. X    if ( TREE_EMPTY( hp ) )
  394. X        return( NULL_OBJ ) ;
  395. X    FIND_MAXIMUM( hp, ROOT( hp ), np ) ;
  396. X    HINT_SET( hp, last_predecessor, np ) ;
  397. X    return( OBJ( np ) ) ;
  398. X}
  399. X
  400. X
  401. X/*
  402. X * When there is no next node, this function returns ANCHOR( hp )
  403. X */
  404. XPRIVATE tnode_s *next_node( hp, x )
  405. X    register header_s    *hp ;
  406. X    register tnode_s    *x ;
  407. X{
  408. X    register tnode_s        *px ;
  409. X    register tnode_s        *next ;
  410. X
  411. X    if ( RIGHT( x ) != NIL( hp ) )
  412. X    {
  413. X        FIND_MINIMUM( hp, RIGHT( x ), next ) ;
  414. X    }
  415. X    else
  416. X    {
  417. X        px = PARENT( x ) ;
  418. X        /*
  419. X         * This loop will end at the root since the root is the *left*
  420. X         * child of the anchor
  421. X         */
  422. X        while ( x == RIGHT( px ) )
  423. X        {
  424. X            x = px ;
  425. X            px = PARENT( x ) ;
  426. X        }
  427. X        next = px ;
  428. X    }
  429. X    return( next ) ;
  430. X}
  431. X
  432. X
  433. X/*
  434. X * Returns a pointer to the object with the next >= key value or
  435. X * NULL_OBJ if the given object is the last one on the tree.
  436. X * It is an error to apply this function to an empty tree.
  437. X */
  438. Xdict_obj bst_successor( handle, object )
  439. X    dict_h        handle ;
  440. X    dict_obj        object ;
  441. X{
  442. X    register header_s        *hp    = THP( handle ) ;
  443. X    dheader_s                *dhp    = DHP( hp ) ;
  444. X    register tnode_s        *x ;
  445. X    register tnode_s        *successor ;
  446. X    char                        *id = "bst_successor" ;
  447. X
  448. X    if ( object == NULL_OBJ )
  449. X        HANDLE_ERROR( dhp, id, DICT_ENULLOBJECT, NULL_OBJ ) ;
  450. X
  451. X    if ( TREE_EMPTY( hp ) )
  452. X        HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
  453. X
  454. X    if ( HINT_MATCH( hp, last_successor, object ) )
  455. X        x = HINT_GET( hp, last_successor ) ;
  456. X    else
  457. X    {
  458. X        x = find_object( hp, object ) ;
  459. X        if ( x == NIL( hp ) )
  460. X            HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
  461. X    }
  462. X
  463. X    successor = next_node( hp, x ) ;
  464. X
  465. X    HINT_SET( hp, last_successor, successor ) ;
  466. X    ERRNO( DHP( hp ) ) = DICT_ENOERROR ;        /* in case we return NULL_OBJ */
  467. X    return( OBJ( successor ) ) ;
  468. X}
  469. X
  470. X
  471. X
  472. X/*
  473. X * When there is no next node, this function returns ANCHOR( hp )
  474. X */
  475. XPRIVATE tnode_s *previous_node( hp, x )
  476. X    register header_s    *hp ;
  477. X    register tnode_s    *x ;
  478. X{
  479. X    register tnode_s        *px ;
  480. X    register tnode_s        *previous ;
  481. X
  482. X    if ( LEFT( x ) != NIL( hp ) )
  483. X    {
  484. X        FIND_MAXIMUM( hp, LEFT( x ), previous ) ;
  485. X    }
  486. X    else
  487. X    {
  488. X        /*
  489. X         * XXX:    To avoid testing against the ANCHOR we can temporarily make the
  490. X         *         root of the tree the *right* child of the anchor
  491. X         */
  492. X        px = PARENT( x ) ;
  493. X        while ( px != ANCHOR( hp ) && x == LEFT( px ) )
  494. X        {
  495. X            x = px ;
  496. X            px = PARENT( x ) ;
  497. X        }
  498. X        previous = px ;
  499. X    }
  500. X    return( previous ) ;
  501. X}
  502. X    
  503. X
  504. X
  505. X/*
  506. X * Returns a pointer to the object with the next <= key value or
  507. X * NULL if the given object is the first one on the tree.
  508. X * It is an error to apply this function to an empty tree.
  509. X */
  510. Xdict_obj bst_predecessor( handle, object )
  511. X    dict_h        handle ;
  512. X    dict_obj        object ;
  513. X{
  514. X    register header_s        *hp    = THP( handle ) ;
  515. X    dheader_s                *dhp    = DHP( hp ) ;
  516. X    tnode_s                    *predecessor ;
  517. X    register tnode_s        *x ;
  518. X    char                        *id = "bst_predecessor" ;
  519. X
  520. X    if ( object == NULL_OBJ )
  521. X        HANDLE_ERROR( dhp, id, DICT_ENULLOBJECT, NULL_OBJ ) ;
  522. X
  523. X    if ( TREE_EMPTY( hp ) )
  524. X        HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
  525. X
  526. X    if ( HINT_MATCH( hp, last_predecessor, object ) )
  527. X        x = HINT_GET( hp, last_predecessor ) ;
  528. X    else
  529. X    {
  530. X        x = find_object( hp, object ) ;
  531. X        if ( x == NIL( hp ) )
  532. X            HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
  533. X    }
  534. X
  535. X    predecessor = previous_node( hp, x ) ;
  536. X
  537. X    HINT_SET( hp, last_predecessor, predecessor ) ;
  538. X    ERRNO( DHP( hp ) ) = DICT_ENOERROR ;
  539. X    return( OBJ( predecessor ) ) ;
  540. X}
  541. X
  542. X
  543. X
  544. Xvoid bst_iterate( handle, direction )
  545. X    dict_h                    handle ;
  546. X    enum dict_direction    direction ;
  547. X{
  548. X    register header_s        *hp    = THP( handle ) ;
  549. X    struct tree_iterator *tip    = &hp->iter ;
  550. X    tnode_s                    *np ;
  551. X
  552. X    tip->direction = direction ;
  553. X    if ( TREE_EMPTY( hp ) )
  554. X        tip->next = NULL ;
  555. X    else
  556. X    {
  557. X        if ( direction == DICT_FROM_START )
  558. X        {
  559. X            FIND_MINIMUM( hp, ROOT( hp ), np ) ;
  560. X        }
  561. X        else
  562. X        {
  563. X            FIND_MAXIMUM( hp, ROOT( hp ), np ) ;
  564. X        }
  565. X        tip->next = np ;
  566. X    }
  567. X}
  568. X
  569. X
  570. Xdict_obj bst_nextobj( handle )
  571. X    dict_h        handle ;
  572. X{
  573. X    register header_s        *hp        = THP( handle ) ;
  574. X    struct tree_iterator *tip        = &hp->iter ;
  575. X    tnode_s                    *current = tip->next ;
  576. X
  577. X    if ( current == NULL )
  578. X        return( NULL_OBJ ) ;
  579. X
  580. X    if ( tip->direction == DICT_FROM_START )
  581. X        tip->next = next_node( hp, current ) ;
  582. X    else
  583. X        tip->next = previous_node( hp, current ) ;
  584. X
  585. X    if ( tip->next == ANCHOR( hp ) )
  586. X        tip->next = NULL ;
  587. X    return( OBJ( current ) ) ;
  588. X}
  589. X
  590. X
  591. X#ifdef BST_DEBUG
  592. X
  593. X#include <stdio.h>
  594. X
  595. X
  596. XPRIVATE void preorder( hp, np, action )
  597. X    header_s        *hp ;
  598. X    tnode_s        *np ;
  599. X    void            (*action)() ;
  600. X{
  601. X    if ( np == NIL( hp ) )
  602. X        return ;
  603. X
  604. X    (*action)( OBJ( np ) ) ;
  605. X    preorder( hp, LEFT( np ), action ) ;
  606. X    preorder( hp, RIGHT( np ), action ) ;
  607. X}
  608. X
  609. X
  610. XPRIVATE void inorder( hp, np, action )
  611. X    header_s        *hp ;
  612. X    tnode_s        *np ;
  613. X    void            (*action)() ;
  614. X{
  615. X    if ( np == NIL( hp ) )
  616. X        return ;
  617. X
  618. X    inorder( hp, LEFT( np ), action ) ;
  619. X    (*action)( OBJ( np ) ) ;
  620. X    inorder( hp, RIGHT( np ), action ) ;
  621. X}
  622. X
  623. X
  624. XPRIVATE void postorder( hp, np, action )
  625. X    header_s        *hp ;
  626. X    tnode_s        *np ;
  627. X    void            (*action)() ;
  628. X{
  629. X    if ( np == NIL( hp ) )
  630. X        return ;
  631. X
  632. X    postorder( hp, LEFT( np ), action ) ;
  633. X    postorder( hp, RIGHT( np ), action ) ;
  634. X    (*action)( OBJ( np ) ) ;
  635. X}
  636. X
  637. X
  638. Xvoid bst_traverse( handle, order, action )
  639. X    dict_h        handle ;
  640. X    bst_order_e    order ;
  641. X    void            (*action)() ;
  642. X{
  643. X    header_s *hp = THP( handle ) ;
  644. X
  645. X    switch ( order )
  646. X    {
  647. X        case BST_INORDER:
  648. X            inorder( hp, ROOT( hp ), action ) ;
  649. X            break ;
  650. X
  651. X        case BST_PREORDER:
  652. X            preorder( hp, ROOT( hp ), action ) ;
  653. X            break ;
  654. X        
  655. X        case BST_POSTORDER:
  656. X            postorder( hp, ROOT( hp ), action ) ;
  657. X            break ;
  658. X    }
  659. X}
  660. X
  661. X
  662. X#ifdef MIN
  663. X#undef MIN
  664. X#endif
  665. X#define MIN( a, b )        ( a < b ? a : b )
  666. X
  667. X#ifdef MAX
  668. X#undef MAX
  669. X#endif
  670. X#define MAX( a, b )        ( a > b ? a : b )
  671. X
  672. Xvoid get_depth( hp, np, dp )
  673. X    header_s                *hp ;
  674. X   tnode_s           *np ;
  675. X   struct bst_depth    *dp ;
  676. X{
  677. X   struct bst_depth    ldep, rdep ;
  678. X
  679. X   if ( np == NIL( hp ) )
  680. X   {
  681. X      dp->depth_min = dp->depth_max = 0 ;
  682. X      return ;
  683. X   }
  684. X   get_depth( hp, LEFT( np ), &ldep ) ;
  685. X   get_depth( hp, RIGHT( np ), &rdep ) ;
  686. X   dp->depth_min = MIN( ldep.depth_min, rdep.depth_min ) + 1 ;
  687. X   dp->depth_max = MAX( ldep.depth_max, rdep.depth_max ) + 1 ;
  688. X    if ( DHP( hp )->flags & DICT_BALANCED_TREE )
  689. X    {
  690. X        if ( dp->depth_max > 2*dp->depth_min )
  691. X            (void) fprintf( stderr, "tree is not balanced\n" ) ;
  692. X    }
  693. X}
  694. X
  695. Xvoid bst_getdepth( handle, dp )
  696. X    dict_h                handle ;
  697. X    struct bst_depth    *dp ;
  698. X{
  699. X    header_s     *hp = THP( handle ) ;
  700. X
  701. X    get_depth( hp, ROOT( hp ), dp ) ;
  702. X}
  703. X
  704. X#endif    /* BST_DEBUG */
  705. X
  706. END_OF_FILE
  707. if test 14392 -ne `wc -c <'libs/src/dict/bst.c'`; then
  708.     echo shar: \"'libs/src/dict/bst.c'\" unpacked with wrong size!
  709. fi
  710. # end of 'libs/src/dict/bst.c'
  711. fi
  712. if test -f 'libs/src/sio/sio.3' -a "${1}" != "-c" ; then 
  713.   echo shar: Will not clobber existing file \"'libs/src/sio/sio.3'\"
  714. else
  715. echo shar: Extracting \"'libs/src/sio/sio.3'\" \(13341 characters\)
  716. sed "s/^X//" >'libs/src/sio/sio.3' <<'END_OF_FILE'
  717. X.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis
  718. X.\"All rights reserved.  The file named COPYRIGHT specifies the terms 
  719. X.\"and conditions for redistribution.
  720. X.\"
  721. X.\" $Id: sio.3,v 8.2 1993/11/22 19:00:24 panos Exp $
  722. X.TH SIO 3X "29 May 1992"
  723. X.SH NAME
  724. XSread, Sgetc, Srdline, Sfetch, Swrite, Sputc, Sprint, Sprintv, Sdone, Sundo, Stie, Suntie, Sflush, Sclose, Sbuftype, Smorefds, Sgetchar, Sputchar, SIOLINELEN, SIOMAXLINELEN - fast stream I/O
  725. X.SH SYNOPSIS
  726. X.LP
  727. X.nf
  728. X.ft B
  729. X#include "sio.h"
  730. X.LP
  731. X.ft B
  732. Xint Sread( fd, buf, nbytes )
  733. Xint fd ;
  734. Xchar *buf ;
  735. Xint nbytes ;
  736. X.LP
  737. X.ft B
  738. Xint Sgetc( fd )
  739. Xint fd ;
  740. X.LP
  741. X.ft B
  742. Xchar *Srdline( fd )
  743. Xint fd ;
  744. X.LP
  745. X.ft B
  746. Xchar *Sfetch( fd, length )
  747. Xint fd ;
  748. Xlong *length ;
  749. X.LP
  750. X.ft B
  751. Xint Swrite( fd, buf, nbytes )
  752. Xint fd ;
  753. Xchar *buf ;
  754. Xint nbytes ;
  755. X.LP
  756. X.ft B
  757. Xint Sputc( fd, c )
  758. Xint fd ;
  759. Xchar c ;
  760. X.LP
  761. X.ft B
  762. Xint Sprint( fd, format [ , ... ] )
  763. Xint fd ;
  764. Xchar *format ;
  765. X.LP
  766. X.ft B
  767. Xint Sprintv( fd, format, ap )
  768. Xint fd ;
  769. Xchar *format ;
  770. Xva_list ap ;
  771. X.LP
  772. X.ft B
  773. Xint Sdone( fd )
  774. Xint fd ;
  775. X.LP
  776. X.ft B
  777. Xint Sundo( fd, type )
  778. Xint fd ;
  779. Xint type ;
  780. X.LP
  781. X.ft B
  782. Xint Stie( ifd, ofd )
  783. Xint ifd, ofd ;
  784. X.LP
  785. X.ft B
  786. Xint Suntie( fd )
  787. Xint fd ;
  788. X.LP
  789. X.ft B
  790. Xint Sbuftype( fd, type )
  791. Xint fd, type ;
  792. X.LP
  793. X.ft B
  794. Xint Smorefds()
  795. X.LP
  796. X.ft B
  797. Xint Sflush( fd )
  798. Xint fd ;
  799. X.LP
  800. X.ft B
  801. Xint Sclose( fd )
  802. Xint fd ;
  803. X.LP
  804. X.ft B
  805. Xint Sgetchar( fd )
  806. Xint fd ;
  807. X.LP
  808. X.ft B
  809. Xint Sputchar( fd, c )
  810. Xint fd;
  811. Xchar c ;
  812. X.LP
  813. X.ft B
  814. Xint SIOLINELEN( fd )
  815. Xint fd ;
  816. X.LP
  817. X.ft B
  818. Xint SIOMAXLINELEN( fd )
  819. Xint fd ;
  820. X.SH DESCRIPTION
  821. XThe \fISIO\fR library provides support
  822. Xfor \fIstream\fR I/O on file descriptors.
  823. XThe first argument of every function
  824. Xor macro is a file descriptor. The file descriptor may be used either for
  825. Xinput or for output, but not both. Attempting to use a descriptor for
  826. Xboth input and output will cause the call for the latter use to fail.
  827. XWhen you are
  828. Xdone with using a file descriptor, you should inform \fISIO\fR
  829. Xby invoking \fBSdone()\fR (unless the program is about to 
  830. Xcall \fIexit(3)\fR).
  831. XYou can also use \fBSdone()\fR if
  832. Xyou want to perform a different type of operation on the same
  833. Xfile descriptor (e.g. first you were reading data from the file
  834. Xdescriptor and then you want to write some data).
  835. XAnother possibility is to do stream I/O at different file offsets
  836. Xby using \fBSdone()\fR before using \fBlseek(2)\fR to move to a
  837. Xnew file offset.
  838. X.LP
  839. XI/O operations on different file descriptors do not interfere
  840. X(unless the file descriptors refer to the same file, in which case
  841. Xthe results are undefined).
  842. X.LP
  843. XFor disk files, I/O always starts at the current file offset.
  844. XIf that offset is not a multiple of the preferred block size for file
  845. Xsystem I/O, performance will not be optimal
  846. X(the preferred block size is determined from the
  847. X\fIst_blksize\fR field in \fIstruct stat\fR).
  848. XFor optimal performance, it is recommended that no I/O operations
  849. X(like \fIread(2)\fR or \fIwrite(2)\fR)
  850. Xare applied to the file descriptor if it is to be used by \fISIO\fR.
  851. X.LP
  852. XRead I/O is either buffered, or is done using memory mapping whenever
  853. Xthat is possible and appropriate.
  854. X.LP
  855. XThe library functions that do stream I/O resemble system calls
  856. X(for example \fBSread()\fR resembles \fIread(2)\fR) so that modifying
  857. Xa program that uses the system calls to use the \fISIO\fR functions
  858. Xis easy (e.g. just replace \fIread(2)\fR with \fBSread()\fR; the function
  859. Xsignatures as well as the return values are exactly the same; also make
  860. Xsure to replace calls to \fIclose(2)\fP with \fBSclose()\fP).
  861. X.LP
  862. X\fISIO\fR uses the underlying system calls \fIread(2)\fR and \fIwrite(2)\fR
  863. Xto do I/O (except when reading files using memory mapping).
  864. XThese calls may be interrupted (i.e. they may return -1 with
  865. X.I errno
  866. Xset to EINTR). Such interruptions are ignored by \fISIO\fR which
  867. Xsimply reissues the system call
  868. X(this means that a \fISIO\fP call will never fail because the
  869. Xunderlying I/O system call was interrupted).
  870. X.LP
  871. X.B Sread()
  872. Xreads \fInbytes\fR bytes from the stream associated with file 
  873. Xdescriptor \fIfd\fR into the buffer pointed to by \fIbuf\fR.
  874. X.LP
  875. X.B Sgetc()
  876. Xreads a character from the stream
  877. Xassociated with file descriptor \fIfd\fR.
  878. XIt returns \fBSIO_EOF\fR if the end of file has been reached.
  879. X.LP
  880. X.B Sgetchar()
  881. X(a macro) performs exactly the same function as \fBSgetc()\fR but
  882. Xit is much faster.
  883. X.LP
  884. X.B Srdline()
  885. Xreads a line from the stream
  886. Xassociated with file descriptor \fIfd\fR.
  887. XThe newline at the end of the line is replaced by a NUL byte. Lines
  888. Xlonger than the maximum line length supported by \fISIO\fR will
  889. Xhave characters deleted.
  890. X.LP
  891. X.B SIOLINELEN()
  892. X(a macro) returns the length of
  893. Xthe line returned by the last call to \fBSrdline()\fR
  894. X(the value returned by \fBSIOLINELEN()\fR is valid only after
  895. X\fBSrdline()\fR and as long as no other 
  896. X\fISIO\fR calls are performed on that file descriptor).
  897. X.LP
  898. X.B SIOMAXLINELEN()
  899. X(a macro) returns
  900. Xthe maximul line length supported by \fISIO\fR for the file
  901. Xdescriptor. As a side-effect it initializes \fIfd\fR for input.
  902. X.LP
  903. X.B Sfetch()
  904. Xreturns a pointer to data coming from the stream
  905. Xassociated with file
  906. Xdescriptor \fIfd\fR. The amount of data available is indicated
  907. Xby the \fIlength\fR argument. One possible use for this function
  908. Xis to copy files.
  909. X.LP
  910. X.B Swrite()
  911. Xwrites \fInbytes\fR bytes to the stream associated with file
  912. Xdescriptor \fIfd\fR from the buffer pointed to by \fIbuf\fR.
  913. X.LP
  914. X.B Sputc()
  915. Xwrites a single character to the stream
  916. Xassociated with file descriptor \fIfd\fR.
  917. X.LP
  918. X.B Sputchar()
  919. X(a macro) performs exactly the same function as \fBSputc()\fR
  920. Xbut it is much faster.
  921. X.LP
  922. X.B Sprint()
  923. Ximitates the behavior of printf(3) as defined in the
  924. XANSI C Standard. There are some limitations. Check the \fBSprint()\fR
  925. Xman page for more information.
  926. X.LP
  927. X.B Sprintv()
  928. Xis the same as \fBSprint()\fR except that it uses a
  929. X\fIvarargs\fR argument list.
  930. X.LP
  931. X.B Sundo()
  932. Xreturns the characters returned by the last call to
  933. X\fBSrdline()\fR, \fBSgetc()\fR or \fBSgetchar()\fR to the stream
  934. Xso that they can be reread. The \fItype\fR argument to \fBSundo()\fR
  935. Xcan be \fBSIO_UNDO_LINE\fR or \fBSIO_UNDO_CHAR\fR depending
  936. Xon whether the call whose effect needs to be undone was
  937. X\fBSrdline()\fR or \fBSgetc()\fR/\fBSgetchar()\fR respectively.
  938. XThere is no check on
  939. Xwhether the last function invoked on \fIfd\fR was one of the above
  940. Xand the results are undefined if there is no correspondence
  941. Xbetween the \fItype\fR and the last operation on \fIfd\fR.
  942. X(i.e. the result is undefined if you try \fBSIO_UNDO_CHAR\fR 
  943. Xand the last operation was not \fBSgetchar()\fR or \fBSgetc()\fR).
  944. X.LP
  945. X.B Stie()
  946. Xties the file descriptor \fIifd\fR to the file descriptor \fIofd\fR.
  947. XThis means that whenever a \fIread(2)\fR is done on \fIifd\fR, it is
  948. Xpreceded by a \fIwrite(2)\fR on \fIofd\fR.
  949. XFor filters it is useful to do \fIStie( 0, 1 )\fR to maximize concurrency.
  950. XIt is also useful to do the same thing when you issue prompts to the
  951. Xuser and you want the user reply to appear on the same line with the
  952. Xprompt.
  953. X\fIifd\fR, \fIofd\fR  will be initialized for input, output respectively
  954. X(if any of them is initialized, it must be for the appropriate
  955. Xstream type (input or output)).
  956. XIf \fIifd\fR was tied to another file descriptor, the old tie is broken.
  957. X.LP
  958. X.B Suntie()
  959. Xundoes the effect of \fBStie()\fR for the specified input file descriptor.
  960. X.LP
  961. X.B Sbuftype()
  962. Xdetermines the buffering type for the output stream associated with
  963. Xfile descriptor \fIfd\fR.
  964. XBy default output directed to terminals is line buffered, output
  965. Xdirected to file descriptor 2 (standard error) is unbuffered and
  966. Xeverything else is fully buffered.
  967. XPossible values for the \fItype\fR argument are
  968. X.RS
  969. X.TP 15
  970. X.SB SIO_FULLBUF
  971. Xfor full buffering
  972. X.TP
  973. X.SB SIO_LINEBUF
  974. Xfor line buffering
  975. X.TP
  976. X.SB SIO_NOBUF
  977. Xfor no buffering
  978. X.RE
  979. X.LP
  980. X.B Smorefds()
  981. Xshould be used to inform \fBSIO\fR that the number of available file
  982. Xdescriptors has been increased. \fBSIO\fR uses an array of internal
  983. Xstream descriptors which are indexed by the file descriptor number. Some
  984. Xoperating systems (ex. SunOS 4.1[.x]) allow the number of available
  985. Xfile descriptors to vary. If that number is increased beyond its initial
  986. Xvalue \fBSIO\fR needs to know in order to allocate more stream descriptors.
  987. X.LP
  988. X.B Sdone()
  989. Xflushes any buffered output for \fIfd\fR 
  990. Xand releases the \fISIO\fR resources used. \fBSdone()\fR 
  991. Xis useful in case the program needs to reprocess the
  992. Xdata of a file descriptor (assuming the file descriptor corresponds
  993. Xto a file).  The program can call \fBSdone()\fR,
  994. X\fIlseek(2)\fR to the beginning of the file
  995. Xand then proceed to reread the file.
  996. X.LP
  997. X.B Sflush()
  998. Xcauses any buffered stream output to be written to the
  999. Xfile descriptor. If its argument is the special value \fBSIO_FLUSH_ALL\fR
  1000. Xthen all output streams will be flushed.
  1001. X.LP
  1002. X.B Sclose()
  1003. Xcloses a file descriptor used for stream I/O, flushes
  1004. Xany buffered output and releases the \fISIO\fR resources used.
  1005. X.SH EXAMPLES
  1006. X.LP
  1007. XThe following code implements a (poor) substitute for the tee command
  1008. X(it copies standard input to a file as well as to standard output).
  1009. X.ne 10
  1010. X.RS
  1011. X.nf
  1012. X.ft B
  1013. X#include "sio.h"
  1014. X.sp .5
  1015. Xmain( argc, argv )
  1016. X    int argc ;
  1017. X    char *argv[] ;
  1018. X{
  1019. X    char *file = (argc > 1) ? argv[ 1 ] : "tee.file" ;
  1020. X    int fd = creat( file, 0644 ) ;
  1021. X    long length ;
  1022. X    char *s ;
  1023. X.sp .5
  1024. X    while ( s = Sfetch( 0, &length ) )
  1025. X    {
  1026. X        Swrite( 1, s, length ) ;
  1027. X        Swrite( fd, s, length ) ;
  1028. X    }
  1029. X    exit( 0 ) ;
  1030. X}
  1031. X.fi
  1032. X.ft R
  1033. X.RE
  1034. X.SH RETURN VALUES
  1035. X.LP
  1036. X.B Sread()
  1037. Xreturns the number of bytes read on success
  1038. X(0 means end-of-file)
  1039. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1040. X.LP
  1041. X.B Sgetc()
  1042. Xreturns the character read on success,
  1043. XSIO_EOF when the end-of-file is reached,
  1044. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1045. X.LP
  1046. X.B Srdline()
  1047. Xreturns a pointer to the next line on success.
  1048. XOn failure or when the end-of-file is reached it returns
  1049. X.SM NULL.
  1050. XIf the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates
  1051. Xthe error.
  1052. X.LP
  1053. X.B Sfetch()
  1054. Xreturns a pointer to file data on success.
  1055. X(the \fIlength\fR argument indicates how many bytes
  1056. Xare available).
  1057. XOn failure or when the end-of-file is reached it returns
  1058. X.SM NULL.
  1059. XIf the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates
  1060. Xthe error.
  1061. X.LP
  1062. X.B Swrite()
  1063. Xreturns the number of bytes written on success
  1064. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1065. X.LP
  1066. X.B Sputc()
  1067. Xreturns the character it was given as an argument on success
  1068. X.B Sprint()
  1069. Xreturns the number of characters printed on success
  1070. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1071. X.LP
  1072. X.B Sdone()
  1073. Xreturns \fB0\fR on success
  1074. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1075. X.LP
  1076. X.B Sundo()
  1077. Xreturns \fB0\fR on success
  1078. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1079. X.LP
  1080. X.B Stie()
  1081. Xreturns \fB0\fR on success
  1082. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1083. X.LP
  1084. X.B Suntie()
  1085. Xreturns \fB0\fR on success
  1086. Xor \fBSIO_ERR\fR on failure
  1087. X(\fIerrno\fR is set to \fBEBADF\fR if there
  1088. Xwas no tied file descriptor).
  1089. X.LP
  1090. X.B Sbuftype()
  1091. Xreturns \fB0\fR on success
  1092. Xor \fBSIO_ERR\fR on failure
  1093. X(\fIerrno\fR is set to \fBEBADF\fR if this is not an output stream
  1094. Xor to \fBEINVAL\fR if an unknown \fItype\fR is specified).
  1095. X.LP
  1096. X.B Smorefds()
  1097. Xreturns \fB0\fR on success
  1098. Xor \fBSIO_ERR\fR on failure (because of lack of memory).
  1099. X.LP
  1100. X.B Sflush()
  1101. Xreturns \fB0\fR on success
  1102. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1103. X.LP
  1104. X.B Sclose()
  1105. Xreturns \fB0\fR on success
  1106. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1107. X.LP
  1108. X.B Sgetchar()
  1109. Xreturns the character read on success,
  1110. XSIO_EOF when the end-of-file is reached,
  1111. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1112. X.LP
  1113. X.B Sputchar()
  1114. Xreturns the character it was given as an argument on success
  1115. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1116. X.LP
  1117. X.B SIOLINELEN()
  1118. Xreturns the length of the last line read by \fBSrdline()\fR.
  1119. X.LP
  1120. X.B SIOMAXLINELEN()
  1121. Xreturns the length of the longest line supported by \fISIO\fR on success
  1122. Xor \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error).
  1123. X.LP
  1124. XAttempting a read operation on a descriptor opened for writing or vice
  1125. Xversa will cause the operation to fail with \fIerrno\fR set to \fBEBADF\fR.
  1126. X.LP
  1127. XThe first \fISIO\fR operation on a descriptor must be a read or write
  1128. Xoperation. It cannot be a control operation (like \fBSflush()\fR). Such
  1129. Xan operation will fail with \fIerrno\fR set to \fBEBADF\fR.
  1130. X.LP
  1131. X.IP "\fBNOTE 1:\fR" 15
  1132. X\fBStie()\fR is an input/output operation for the
  1133. Xrespective file descriptors, not a control operation. \fBSuntie()\fR
  1134. Xis a control operation.
  1135. X.IP "\fBNOTE 2:\fR"
  1136. X\fBSIO_ERR\fR is defined to be \fB-1\fR.
  1137. X.SH "SEE ALSO"
  1138. X.LP
  1139. XSprint(3)
  1140. X.SH BUGS
  1141. X.LP
  1142. XIf the operating system does not provide for invocation of a
  1143. Xfinalization function upon exit, the program will have to
  1144. Xexplicitly flush all output streams.
  1145. XThe following operating systems provide such a facility:
  1146. XSunOS 4.x, Ultrix 4.x, SunOS 5.x
  1147. X.LP
  1148. XSocket file descriptors can be used for input as well as output but
  1149. X\fBSIO\fR does not support this.
  1150. X.LP
  1151. XThe current implementation will not try to use memory mapping to
  1152. Xread a file if the file offset is not 0 (it will use buffered I/O instead).
  1153. X.LP
  1154. XPointers returned by \fBSfetch()\fR point to read-only memory.
  1155. XAttempting to modify this memory will result in a segmentation
  1156. Xviolation.
  1157. END_OF_FILE
  1158. if test 13341 -ne `wc -c <'libs/src/sio/sio.3'`; then
  1159.     echo shar: \"'libs/src/sio/sio.3'\" unpacked with wrong size!
  1160. fi
  1161. # end of 'libs/src/sio/sio.3'
  1162. fi
  1163. echo shar: End of archive 17 \(of 20\).
  1164. cp /dev/null ark17isdone
  1165. MISSING=""
  1166. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
  1167.     if test ! -f ark${I}isdone ; then
  1168.     MISSING="${MISSING} ${I}"
  1169.     fi
  1170. done
  1171. if test "${MISSING}" = "" ; then
  1172.     echo You have unpacked all 20 archives.
  1173.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1174. else
  1175.     echo You still need to unpack the following archives:
  1176.     echo "        " ${MISSING}
  1177. fi
  1178. ##  End of shell archive.
  1179. exit 0
  1180.