home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / xinetd21 / part18 < prev    next >
Encoding:
Text File  |  1993-06-26  |  29.8 KB  |  1,247 lines

  1. Newsgroups: comp.sources.unix
  2. From: panos@cs.colorado.edu (Panos Tsirigotis)
  3. Subject: v26i262: xinetd-2.1.1 - inetd replacement with access control and logging, Part18/31
  4. Sender: unix-sources-moderator@gw.home.vix.com
  5. Approved: vixie@gw.home.vix.com
  6.  
  7. Submitted-By: panos@cs.colorado.edu (Panos Tsirigotis)
  8. Posting-Number: Volume 26, Issue 262
  9. Archive-Name: xinetd-2.1.1/part18
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 18 (of 31)."
  18. # Contents:  libs/src/sio/suite/print.c libs/src/timer/timer.3
  19. #   xinetd/builtins.c
  20. # Wrapped by panos@mystique on Mon Jun 21 14:51:25 1993
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'libs/src/sio/suite/print.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'libs/src/sio/suite/print.c'\"
  24. else
  25. echo shar: Extracting \"'libs/src/sio/suite/print.c'\" \(8243 characters\)
  26. sed "s/^X//" >'libs/src/sio/suite/print.c' <<'END_OF_FILE'
  27. X/*
  28. X * (c) Copyright 1992, 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: print.c,v 8.1 1993/03/13 01:21:48 panos Exp $" ;
  34. X
  35. X#include <stdio.h>
  36. X#include <math.h>
  37. X#include <values.h>
  38. X#include <string.h>
  39. X
  40. X#include "sio.h"
  41. X
  42. X#define FLUSH()            fflush( stdout ) ; Sflush( 1 )
  43. X#define COMPARE( printf_count, sprint_count )                                        \
  44. X                            if ( printf_count != sprint_count )                            \
  45. X                                printf( "printf_count = %d, sprint_count = %d\n",    \
  46. X                                            printf_count, sprint_count )
  47. X
  48. Xenum bool { NO = 0, YES = 1 } ;
  49. X
  50. Xenum test_flag
  51. X{
  52. X    DECIMAL, HEX, CAP_HEX, OCTAL, UNSIGNED,
  53. X    F_FLOAT, G_FLOAT, E_FLOAT, CAP_E_FLOAT, CAP_G_FLOAT,
  54. X    CHAR, STRING,
  55. X    POINTER,
  56. X    BOUND,
  57. X    N_FLAGS
  58. X} ;
  59. X
  60. Xtypedef enum test_flag FLAG ;
  61. X
  62. X#define CHECK( f )                if ( ! flags[ f ] ) return
  63. X
  64. X/*
  65. X * Flags
  66. X */
  67. Xenum bool flags[ N_FLAGS ] ;
  68. X
  69. Xchar *precision ;
  70. Xchar *width ;
  71. Xchar *print_flags ;
  72. X
  73. Xint i_begin = 123456 ;
  74. Xint i_end = 123470 ;
  75. Xint i_step = 1 ;
  76. X
  77. Xdouble f_begin = 1.234567654312 ;
  78. Xdouble f_end = 2.0 ;
  79. Xdouble f_step = 0.011 ;
  80. X
  81. X#define LEN( s )                    ( s ? strlen( s ) : 0 )
  82. X
  83. Xchar *format( f )
  84. X    char *f ;
  85. X{
  86. X    char *malloc() ;
  87. X    static char *newfmt ;
  88. X
  89. X    if ( newfmt )
  90. X        free( newfmt ) ;
  91. X
  92. X    newfmt = malloc( strlen( f )
  93. X                + LEN( precision ) + LEN( width ) + LEN( print_flags ) + 2 ) ;
  94. X    (void) strcpy( newfmt, "%" ) ;
  95. X    if ( print_flags )
  96. X        (void) strcat( newfmt, print_flags ) ;
  97. X    if ( width )
  98. X        (void) strcat( newfmt, width ) ;
  99. X    if ( precision )
  100. X        (void) strcat( strcat( newfmt, "." ), precision ) ;
  101. X    (void) strcat( newfmt, &f[1] ) ;
  102. X    return( newfmt ) ;
  103. X}
  104. X
  105. X#define decimal_test()            integer_test( "%d %d\n", DECIMAL )
  106. X#define hex_test()                integer_test( "%x %x\n", HEX )
  107. X#define cap_hex_test()            integer_test( "%X %X\n", CAP_HEX )
  108. X#define octal_test()                integer_test( "%o %o\n", OCTAL )
  109. X#define unsigned_test()            integer_test( "%u %u\n", UNSIGNED )
  110. X
  111. Xvoid integer_test( fmt, flag )
  112. X    char *fmt ;
  113. X    FLAG flag ;
  114. X{
  115. X    int i ;
  116. X    int ccs, ccp ;
  117. X
  118. X    CHECK( flag ) ;
  119. X    fmt = format( fmt ) ;
  120. X
  121. X    for ( i = i_begin ; i < i_end ; i += i_step )
  122. X    {
  123. X        ccp = printf( fmt, -i, i ) ;
  124. X        ccs = Sprint( 2, fmt, -i, i ) ;
  125. X        FLUSH() ;
  126. X        COMPARE( ccp, ccs ) ;
  127. X    }
  128. X}
  129. X
  130. X
  131. X#define f_float_test()            fp_test( "%f\n", F_FLOAT )
  132. X#define g_float_test()            fp_test( "%g\n", G_FLOAT )
  133. X#define e_float_test()            fp_test( "%e\n", E_FLOAT )
  134. X#define cap_e_float_test()        fp_test( "%E\n", CAP_E_FLOAT )            
  135. X#define cap_g_float_test()        fp_test( "%G\n", CAP_G_FLOAT )
  136. X
  137. Xvoid fp_test( fmt, flag )
  138. X    char *fmt ;
  139. X    FLAG flag ;
  140. X{
  141. X    double d ;
  142. X    double step ;
  143. X    int ccs, ccp ;
  144. X
  145. X    CHECK( flag ) ;
  146. X    fmt = format( fmt ) ;
  147. X    
  148. X    for ( d = f_begin, step = f_step ; d < f_end ; d += step, step += step )
  149. X    {
  150. X
  151. X        ccp = printf( fmt, d ) ;
  152. X        ccs = Sprint( 2, fmt, d ) ;
  153. X        FLUSH() ;
  154. X        COMPARE( ccp, ccs ) ;
  155. X    }
  156. X}
  157. X
  158. X
  159. Xvoid char_test()
  160. X{
  161. X    char *s = "foobar" ;
  162. X    int len = strlen( s ) ;
  163. X    int i ;
  164. X    char *fmt = "%c\n" ;
  165. X    int ccs, ccp ;
  166. X
  167. X    CHECK( CHAR ) ;
  168. X    fmt = format( fmt ) ;
  169. X
  170. X    for ( i = 0 ; i < len ; i++ )
  171. X    {
  172. X        ccp = printf( fmt, s[ i ] ) ;
  173. X        ccs = Sprint( 2, fmt, s[ i ] ) ;
  174. X        FLUSH() ;
  175. X        COMPARE( ccp, ccs ) ;
  176. X    }
  177. X}
  178. X
  179. X
  180. Xvoid string_test()
  181. X{
  182. X    static char *list[] = 
  183. X    {
  184. X        "foobar",
  185. X        "hello",
  186. X        "world",
  187. X        "this is a very long string, a really long string, really, true, honest",
  188. X        "i am getting tired of this",
  189. X        "SO THIS IS THE END",
  190. X        0
  191. X    } ;
  192. X    char *fmt = "%s\n" ;
  193. X    char **p ;
  194. X    int ccp, ccs ;
  195. X
  196. X    CHECK( STRING ) ;
  197. X    fmt = format( fmt ) ;
  198. X
  199. X    for ( p = &list[ 0 ] ; *p ; p++ )
  200. X    {
  201. X        ccp = printf( fmt, *p ) ;
  202. X        ccs = Sprint( 2, fmt, *p ) ;
  203. X        FLUSH() ;
  204. X        COMPARE( ccp, ccs ) ;
  205. X    }
  206. X}
  207. X
  208. X
  209. Xvoid pointer_test()
  210. X{
  211. X    struct foo
  212. X    {
  213. X        char bar1 ;
  214. X        short bar2 ;
  215. X        int bar3 ;
  216. X        long bar4 ;
  217. X        char *bar5 ;
  218. X    } foo, *end = &foo, *p ;
  219. X    char *fmt = "%p\n" ;
  220. X    int ccp, ccs ;
  221. X
  222. X    CHECK( POINTER ) ;
  223. X    fmt = format( fmt ) ;
  224. X
  225. X    end += 10 ;
  226. X    for ( p = &foo ; p < end ; p++ )
  227. X    {
  228. X        ccp = printf( fmt, p ) ;
  229. X        ccs = Sprint( 2, fmt, p ) ;
  230. X        FLUSH() ;
  231. X    }
  232. X}
  233. X
  234. X
  235. X/* 
  236. X * bound_test is only available on SunOS 4.x
  237. X */
  238. X#if defined( sun )
  239. X
  240. Xvoid bound_test()
  241. X{
  242. X    char *fmt ;
  243. X    double bound_values[ 10 ] ;
  244. X    static char *bound_names[] =
  245. X    {
  246. X        "min_subnormal",
  247. X        "max_subnormal",
  248. X        "min_normal",
  249. X        "max_normal",
  250. X        "infinity",
  251. X        "quiet_nan",
  252. X        "signaling_nan"
  253. X    } ;
  254. X    int n_values ;
  255. X    int i ;
  256. X    int ccp, ccs ;
  257. X
  258. X    bound_values[ 0 ] = min_subnormal() ;
  259. X    bound_values[ 1 ] = max_subnormal() ;
  260. X    bound_values[ 2 ] = min_normal() ;
  261. X    bound_values[ 3 ] = max_normal() ;
  262. X    bound_values[ 4 ] = infinity() ;
  263. X    bound_values[ 5 ] = quiet_nan( 7L ) ;
  264. X    bound_values[ 6 ] = signaling_nan( 7L ) ;
  265. X    n_values = 7 ;
  266. X
  267. X    CHECK( BOUND ) ;
  268. X
  269. X    for ( i = 0 ; i < n_values ; i++ )
  270. X    {
  271. X        double d = bound_values[ i ] ;
  272. X        char *name = bound_names[ i ] ;
  273. X
  274. X        fmt = format( "%f (%s)\n" ) ;
  275. X        ccp = printf( fmt, d, name ) ;
  276. X        ccs = Sprint( 2, fmt, d, name ) ;
  277. X        FLUSH() ;
  278. X        COMPARE( ccp, ccs ) ;
  279. X
  280. X        fmt = format( "%e (%s)\n" ) ;
  281. X        ccp = printf( fmt, d, name ) ;
  282. X        ccs = Sprint( 2, fmt, d, name ) ;
  283. X        FLUSH() ;
  284. X        COMPARE( ccp, ccs ) ;
  285. X
  286. X        fmt = format( "%g (%s)\n" ) ;
  287. X        ccp = printf( fmt, d, name ) ;
  288. X        ccs = Sprint( 2, fmt, d, name ) ;
  289. X        FLUSH() ;
  290. X        COMPARE( ccp, ccs ) ;
  291. X    }
  292. X
  293. X    fmt = format( "%d (MININT)\n" ) ;
  294. X    ccp = printf( fmt, -MAXINT-1 ) ;
  295. X    ccs = Sprint( 2, fmt, -MAXINT-1 ) ;
  296. X    COMPARE( ccp, ccs ) ;
  297. X}
  298. X#else
  299. Xvoid bound_test()
  300. X{
  301. X}
  302. X#endif
  303. X
  304. X
  305. Xint get_options( argc, argv )
  306. X    int argc ;
  307. X    char *argv[] ;
  308. X{
  309. X    int arg_index = 1 ;
  310. X    char *p ;
  311. X    double atof() ;
  312. X
  313. X    for ( arg_index = 1 ;
  314. X            arg_index < argc && argv[ arg_index ][ 0 ] == '-' ; arg_index++ )
  315. X    {
  316. X        switch ( argv[ arg_index ][ 1 ] )
  317. X        {
  318. X            case 'd':
  319. X                flags[ DECIMAL ] = YES ;
  320. X                break ;
  321. X            
  322. X            case 'x':
  323. X                flags[ HEX ] = YES ;
  324. X                break ;
  325. X            
  326. X            case 'X':
  327. X                flags[ CAP_HEX ] = YES ;
  328. X                break ;
  329. X            
  330. X            case 'o':
  331. X                flags[ OCTAL ] = YES ;
  332. X                break ;
  333. X            
  334. X            case 'u':
  335. X                flags[ UNSIGNED ] = YES ;
  336. X                break ;
  337. X
  338. X            case 'f':
  339. X                flags[ F_FLOAT ] = YES ;
  340. X                break ;
  341. X            
  342. X            case 'g':
  343. X                flags[ G_FLOAT ] = YES ;
  344. X                break ;
  345. X            
  346. X            case 'e':
  347. X                flags[ E_FLOAT ] = YES ;
  348. X                break ;
  349. X            
  350. X            case 'E':
  351. X                flags[ CAP_E_FLOAT ] = YES ;
  352. X                break ;
  353. X            
  354. X            case 'G':
  355. X                flags[ CAP_G_FLOAT ] = YES ;
  356. X                break ;
  357. X
  358. X            case 'c':
  359. X                flags[ CHAR ] = YES ;
  360. X                break ;
  361. X            
  362. X            case 's':
  363. X                flags[ STRING ] = YES ;
  364. X                break ;
  365. X            
  366. X            case 'p':
  367. X                flags[ POINTER ] = YES ;
  368. X                break ;
  369. X            
  370. X            case 'b':        /* this is for checking bounds in fp formats */
  371. X                flags[ BOUND ] = YES ;
  372. X                break ;
  373. X                
  374. X            case 'P':    /* precision, must be followed by a number, e.g. -P10 */
  375. X                precision = &argv[ arg_index ][ 2 ] ;
  376. X                break ;
  377. X            
  378. X            case 'W':    /* width, must be followed by a number, e.g. -w10 */
  379. X                width = &argv[ arg_index ][ 2 ] ;
  380. X                break ;
  381. X            
  382. X            case 'F':    /* flags, whatever is after the F */
  383. X                print_flags = &argv[ arg_index ][ 2 ] ;
  384. X                break ;
  385. X            
  386. X            /*
  387. X             * Options recognized in this case:    -Vf, -Vi
  388. X             * Usage: -V[if] start end step
  389. X             */
  390. X            case 'V':
  391. X                /*
  392. X                 * Check if we have enough extra arguments
  393. X                 */
  394. X                if ( argc - ( arg_index + 1 ) < 3 )
  395. X                {
  396. X                    fprintf( stderr, "Insufficient # of args after V option\n" ) ;
  397. X                    exit( 1 ) ;
  398. X                }
  399. X                switch ( argv[ arg_index ][ 2 ] )
  400. X                {
  401. X                    case 'f':
  402. X                        f_begin = atof( argv[ arg_index+1 ] ) ;
  403. X                        f_end   = atof( argv[ arg_index+2 ] ) ;
  404. X                        f_step  = atof( argv[ arg_index+3 ] ) ;
  405. X                        break ;
  406. X                    
  407. X                    case 'i':
  408. X                        i_begin = atoi( argv[ arg_index+1 ] ) ;
  409. X                        i_end   = atoi( argv[ arg_index+2 ] ) ;
  410. X                        i_step  = atoi( argv[ arg_index+3 ] ) ;
  411. X                        break ;
  412. X                }
  413. X                arg_index += 3 ;
  414. X                break ;
  415. X
  416. X            case 'S':
  417. X                f_step = atof( &argv[ arg_index ][ 2 ] ) ;
  418. X                break ;
  419. X        }
  420. X    }
  421. X    return( arg_index ) ;
  422. X}
  423. X
  424. X
  425. X#define EQ( s1, s2 )                ( strcmp( s1, s2 ) == 0 )
  426. X
  427. X
  428. Xint main( argc, argv )
  429. X    int argc ;
  430. X    char *argv[] ;
  431. X{
  432. X
  433. X    if ( Sbuftype( 2, SIO_LINEBUF ) == SIO_ERR )
  434. X    {
  435. X        char *msg = "Sbuftype failed\n" ;
  436. X
  437. X        write( 2, msg, strlen( msg ) ) ;
  438. X        exit( 1 ) ;
  439. X    }
  440. X
  441. X    if ( argc == 1 || argc == 2 && EQ( argv[ 1 ], "ALL" ) )
  442. X    {
  443. X        /* perform all tests */
  444. X        int i ;
  445. X
  446. X        for ( i = 0 ; i < N_FLAGS ; i++ )
  447. X            flags[ i ] = YES ;
  448. X    }
  449. X    else
  450. X        (void) get_options( argc, argv ) ;
  451. X
  452. X    decimal_test() ;
  453. X    hex_test() ;
  454. X    cap_hex_test() ;
  455. X    octal_test() ;
  456. X    unsigned_test() ;
  457. X
  458. X    f_float_test() ;
  459. X    g_float_test() ;
  460. X    e_float_test() ;
  461. X    cap_g_float_test() ;
  462. X    cap_e_float_test() ;
  463. X
  464. X    string_test() ;
  465. X    char_test() ;
  466. X    pointer_test() ;
  467. X    bound_test() ;
  468. X    exit( 0 ) ;
  469. X}
  470. END_OF_FILE
  471. if test 8243 -ne `wc -c <'libs/src/sio/suite/print.c'`; then
  472.     echo shar: \"'libs/src/sio/suite/print.c'\" unpacked with wrong size!
  473. fi
  474. # end of 'libs/src/sio/suite/print.c'
  475. fi
  476. if test -f 'libs/src/timer/timer.3' -a "${1}" != "-c" ; then 
  477.   echo shar: Will not clobber existing file \"'libs/src/timer/timer.3'\"
  478. else
  479. echo shar: Extracting \"'libs/src/timer/timer.3'\" \(9383 characters\)
  480. sed "s/^X//" >'libs/src/timer/timer.3' <<'END_OF_FILE'
  481. X.\"(c) Copyright 1993 by Panagiotis Tsirigotis
  482. X.\"All rights reserved.  The file named COPYRIGHT specifies the terms 
  483. X.\"and conditions for redistribution.
  484. X.\"
  485. X.\" $Id: timer.3,v 4.3 1993/06/01 05:54:56 panos Exp $
  486. X.TH TIMER 3X "20 April 1993"
  487. X.SH NAME
  488. Xtimer_create, timer_destroy, timer_start, timer_stop, timer_block, timer_unblock, timer_expirations, timer_block_type, timer_unblock_type - timer management functions
  489. X.SH SYNOPSIS
  490. X.LP
  491. X.nf
  492. X.ft B
  493. X#include "timer.h"
  494. X.LP
  495. X.ft B
  496. Xenum timer_types { TIMER_REAL, TIMER_VIRTUAL, TIMER_PROF } ;
  497. Xenum timer_timetypes { TIMER_ABSOLUTE, TIMER_RELATIVE } ;
  498. X.LP
  499. X.ft B
  500. Xextern int timer_errno ;
  501. X.LP
  502. X.ft B
  503. Xtimer_h timer_create( type, flags, errnop )
  504. Xenum timer_types type ;
  505. Xint flags ;
  506. Xint *errnop ;
  507. X.LP
  508. X.ft B
  509. Xvoid timer_destroy( handle )
  510. Xtimer_h handle ;
  511. X.LP
  512. X.ft B
  513. Xint timer_start( handle, itvp, time_type, action )
  514. Xtimer_h handle ;
  515. Xstruct itimerval *itvp ;
  516. Xenum timer_timetypes time_type ;
  517. Xstruct timer_action *action ;
  518. X.LP
  519. X.ft B
  520. Xvoid timer_stop( handle )
  521. Xtimer_h handle ;
  522. X.LP
  523. X.ft B
  524. Xvoid timer_block( handle )
  525. Xtimer_h handle ;
  526. X.LP
  527. X.ft B
  528. Xvoid timer_unblock( handle )
  529. Xtimer_h handle ;
  530. X.LP
  531. X.ft B
  532. Xunsigned timer_expirations( handle )
  533. Xtimer_h handle ;
  534. X.LP
  535. X.ft B
  536. Xvoid timer_block_type( type )
  537. Xenum timer_types type ;
  538. X.LP
  539. X.ft B
  540. Xvoid timer_unblock_type( type )
  541. Xenum timer_types type ;
  542. X.SH DESCRIPTION
  543. X.LP
  544. XThis library provides support for multiple timers of various types by
  545. Xmultiplexing the timers provided by the operating system.
  546. XAn action is associated with each timer.
  547. XThe action can either be a function that will be invoked when the
  548. Xtimer expires or
  549. Xit can be an integer variable that will either be set to
  550. X.SM TRUE
  551. Xwhen the timer expires or it will be increased for every expiration
  552. Xof the timer (it is up to the user to clear the variable after inspecting
  553. Xits value).
  554. XIf a function is used, then it is possible that while that function is running,
  555. Xmore timers may expire and their functions be called 
  556. X(i.e. timer interrupts are not blocked while the function is running).
  557. XThe timer-action association can change through the lifetime of the timer.
  558. XThe timer types supported by this library depend on the operating system.
  559. X.\" *********************** timer_create *****************************
  560. X.LP
  561. X.B timer_create()
  562. Xcreates a timer and returns a handle to it. When the timer is created, it
  563. Xis inactive. Possible values for \fItype\fP are:
  564. X.TP 18
  565. X.SB TIMER_REAL
  566. Xtimers of this type are wall-clock timers
  567. X.TP
  568. X.SB TIMER_VIRTUAL
  569. Xtimers of this type are running only when the process is running
  570. X.TP
  571. X.SB TIMER_PROF
  572. Xtimers of this type are running only when the process is running 
  573. Xor the system is doing work on behalf of the process.
  574. X.LP
  575. XPossible values for \fIflags\fP are formed by ORing any of the following
  576. Xconstants:
  577. X.TP 20
  578. X.SB TIMER_RETURN_ERROR
  579. Xif an error
  580. Xoccurs while handling \fIthis\fP timer an error indication will be returned
  581. X(the default is program termination)
  582. X.LP
  583. XYou can use the constant
  584. X.B TIMER_NOFLAGS
  585. Xto specify no flags at all.
  586. XThe argument
  587. X.I errnop
  588. Xis a pointer to a variable where error values will be placed
  589. X(the default, in case 
  590. X.I errnop
  591. Xis
  592. X.SM NULL
  593. Xis \fItimer_errno\fP).
  594. X.\" *********************** timer_destroy *****************************
  595. X.LP
  596. X.B timer_destroy()
  597. Xdestroys the timer specified by \fIhandle\fP. 
  598. XIf the timer is active, it is deactivated first.
  599. X.\" *********************** timer_start *****************************
  600. X.LP
  601. X.B timer_start()
  602. Xactivates the timer specified by
  603. X.I handle.
  604. XThe expiration time is determined by
  605. X.I itvp
  606. Xand
  607. X.I time_type.
  608. X.I time_type
  609. Xcan be either
  610. X.B TIMER_ABSOLUTE
  611. Xor 
  612. X.B TIMER_RELATIVE.
  613. XTimers of the former
  614. Xtype expire at the time 
  615. Xspecified by
  616. X.I "itvp->it_value"
  617. X(if that time is before the
  618. Xcurrent time then the timer expires immediately) while the expiration
  619. Xtime of timers of the latter type expire at
  620. X.I "current time + itvp->it_value."
  621. X.I "itvp->it_interval"
  622. Xis the interval between subsequent expirations of the timer after the
  623. Xoriginal expiration.
  624. XThe
  625. X.I action
  626. Xargument determines the action to be taken when the timer expires.
  627. X.LP
  628. X.I "struct timer_action"
  629. Xis defined as follows:
  630. X.sp 1
  631. X.PD .1v
  632. X.RS
  633. X.nf
  634. Xstruct timer_action
  635. X{
  636. X.RS
  637. X.IP "int" 15
  638. Xta_flags ;
  639. X.IP "void"
  640. X(*ta_func)() ;
  641. X.IP void
  642. X*ta_arg ;
  643. X.IP jmp_buf
  644. Xta_env ;
  645. X.RE
  646. X} ;
  647. X.RE
  648. X.PD
  649. X.fi
  650. X.LP
  651. XThe type of action taken when a timer expires depends on
  652. X.I ta_func
  653. Xand
  654. X.I ta_arg.
  655. X.RS
  656. X.IP "Case 1: ta_func != NULL"
  657. XThe function will be invoked with 2 arguments: the handle of the expired
  658. Xtimer and
  659. X.I ta_arg.
  660. X.I "Such functions should not use longjmp."
  661. X.IP "Case 2: ta_func == NULL && ta_arg != NULL"
  662. X.I ta_arg
  663. Xis assumed to be a pointer to an integer variable. This variable will
  664. Xeither be set to
  665. X.SM TRUE
  666. Xor its value will be increased.
  667. X.IP "Case 3: ta_func == NULL && ta_arg == NULL"
  668. Xnothing happens.
  669. X.RE
  670. X.sp 1
  671. X.I ta_flags
  672. Xis formed by ORing any of the following constants:
  673. X.TP 20
  674. X.SB TIMER_INC_VAR
  675. XWhen the action associated with the timer is an integer variable then
  676. Xthe variable will be increased by the number of timer expirations (the
  677. Xdefault is for the variable to be set to 1).
  678. X.TP
  679. X.SB TIMER_BLOCK_ALL
  680. XWhen the action associated with the timer is a function, all timer
  681. Xinterrupts will be blocked while the function is running.
  682. X.TP
  683. X.SB TIMER_BLOCK_SAME
  684. XWhen the action associated with the timer is a function, all timers
  685. Xof the same type will be blocked while the function is running.
  686. X.TP
  687. X.SB TIMER_LONGJMP
  688. XA longjmp(3) will be performed using
  689. X.I ta_env
  690. Xafter the action associated with the timer is performed.
  691. X.LP
  692. XYou can use the constant
  693. X.B TIMER_NOFLAGS
  694. Xto specify no flags at all.
  695. X.\" *********************** timer_stop *****************************
  696. X.LP
  697. X.B timer_stop()
  698. Xstops the specified timer. The timer becomes inactive and it cannot
  699. Xbe restarted.
  700. X.\" *********************** timer_block *****************************
  701. X.LP
  702. X.B timer_block()
  703. Xblocks the specified timer. The timer is still active and may expire
  704. Xbut its action will not be executed until the
  705. Xtimer is unblocked.
  706. X.\" *********************** timer_unblock *****************************
  707. X.LP
  708. X.B timer_unblock()
  709. Xunblocks the specified timer. If the timer is past its expiration time,
  710. Xthe associated action will be executed immediately.
  711. X.\" *********************** timer_expirations *****************************
  712. X.LP
  713. X.B timer_expirations()
  714. Xshould be used by the user-specified function to find out 
  715. Xthe number of times the timer expired until the function was called.
  716. XMultiple expirations are possible for a timer that expires periodically.
  717. X.\" *********************** timer_block_type *****************************
  718. X.LP
  719. X.B timer_block_type()
  720. Xblocks all timers of the specified \fItype\fP.
  721. XThis also includes any timers started 
  722. X.I after
  723. Xthis function is invoked.
  724. X.\" *********************** timer_unblock_type *****************************
  725. X.LP
  726. X.B timer_unblock_type()
  727. Xunblocks all timers of the specified \fItype\fP.
  728. X.\" *********************** notes *****************************
  729. X.SH NOTES
  730. X.LP
  731. XAny of the timer operations can be used on any timer handle at any time.
  732. XSpecifically, the operations can be used from within the functions
  733. Xinvoked when the timers expire.
  734. X.LP
  735. XThe function associated with a timer is not
  736. Xinvoked for each expiration of that timer.
  737. XIt is possible for a timer to expire
  738. Xmultiple times while its function is running (because timers can
  739. Xbe scheduled to expire periodically) or while other timer functions
  740. Xare running. The timer function can find out how many times its timer
  741. Xhas expired by using the
  742. X.B timer_expirations()
  743. Xoperation.
  744. X.LP
  745. XIt is guaranteed that the function associated with a specific timer will 
  746. Xnot be called recursively if that timer expires multiple times.
  747. X.LP
  748. XThe order of execution of timer-associated functions for
  749. Xtimers that expire at the same time is undefined.
  750. X.LP
  751. XEven if a function returns a jmp_buf pointer, it is not guaranteed that
  752. Xthe pointer will be used. The reason is that more than one function
  753. Xcan expire at the same time and return a jmp_buf pointer. Since there
  754. Xcan be only one control flow, only one of those jmp_buf's can be used and
  755. Xthe choice between them is implementation-dependent.
  756. X.SH "RETURN VALUES"
  757. X.LP
  758. XValues for 
  759. X.I timer_errno
  760. Xwill be stored in the user-specified variable if one was provided.
  761. X.LP
  762. X.B timer_create()
  763. Xreturns a timer handle if successful or
  764. X.SM NULL
  765. Xif it fails.
  766. X.LP
  767. X.B timer_start()
  768. Xreturns 
  769. X.B TIMER_OK
  770. Xif successful or
  771. X.B TIMER_ERR
  772. Xif it fails.
  773. X.LP
  774. X.B timer_expirations()
  775. Xreturns a positive (non-zero) number when invoked from a timer-associated
  776. Xfunction; otherwise its return value is undefined.
  777. X.RE
  778. X.SH "ERRORS"
  779. XThe following is a list of error codes which will be placed in
  780. X.I timer_errno
  781. Xor the user-specified variable when a timer operation fails:
  782. X.RS
  783. X.TP 20
  784. X.SB TIMER_ENOMEM
  785. XA memory allocation request failed.
  786. X.TP
  787. X.SB TIMER_EBADTYPE
  788. XAn unknown timer type was specified.
  789. X.TP
  790. X.SB TIMER_ESIGPROBLEM
  791. XA signal handler could not be installed.
  792. X.TP
  793. X.SB TIMER_EBADSTATE
  794. XThe timer state does not allow this operation (for example, the timer is
  795. Xrunning and the operation attempted was \fBtimer_start()\fP).
  796. X.TP
  797. X.SB TIMER_EBADTIME
  798. XThe time value specified was negative.
  799. X.TP
  800. X.SB TIMER_ENOTAVAILABLE
  801. XThe requested timer type is not available.
  802. X.TP
  803. X.SB TIMER_ECANTINSERT
  804. XThe insertion of this timer in the queue of timers failed.
  805. X.TP
  806. X.SB TIMER_SIGPROBLEM
  807. XThere was an error while trying to install a signal handler.
  808. X.SH "SEE ALSO"
  809. Xsetitimer(2), setjmp(3), longjmp(3)
  810. END_OF_FILE
  811. if test 9383 -ne `wc -c <'libs/src/timer/timer.3'`; then
  812.     echo shar: \"'libs/src/timer/timer.3'\" unpacked with wrong size!
  813. fi
  814. # end of 'libs/src/timer/timer.3'
  815. fi
  816. if test -f 'xinetd/builtins.c' -a "${1}" != "-c" ; then 
  817.   echo shar: Will not clobber existing file \"'xinetd/builtins.c'\"
  818. else
  819. echo shar: Extracting \"'xinetd/builtins.c'\" \(8896 characters\)
  820. sed "s/^X//" >'xinetd/builtins.c' <<'END_OF_FILE'
  821. X/*
  822. X * (c) Copyright 1992 by Panagiotis Tsirigotis
  823. X * All rights reserved.  The file named COPYRIGHT specifies the terms 
  824. X * and conditions for redistribution.
  825. X */
  826. X
  827. Xstatic char RCSid[] = "$Id: builtins.c,v 6.9 1993/06/15 23:25:57 panos Exp $" ;
  828. X
  829. X#include <sys/types.h>
  830. X#include <sys/socket.h>
  831. X#include <errno.h>
  832. X#include <time.h>
  833. X#include <syslog.h>
  834. X
  835. X#include "str.h"
  836. X
  837. X#include "config.h"
  838. X#include "state.h"
  839. X#include "defs.h"
  840. X#include "builtin.h"
  841. X#include "sconf.h"
  842. X#include "server.h"
  843. X
  844. Xextern int errno ;
  845. X
  846. Xtime_t time() ;
  847. X
  848. Xvoid msg() ;
  849. Xstatus_e write_buf() ;
  850. X
  851. X#define BUFFER_SIZE                    1024
  852. X
  853. XPRIVATE void stream_echo() ;
  854. XPRIVATE void dgram_echo() ;
  855. XPRIVATE void stream_discard() ;
  856. XPRIVATE void dgram_discard() ;
  857. XPRIVATE void stream_time() ;
  858. XPRIVATE void dgram_time() ;
  859. XPRIVATE void stream_daytime() ;
  860. XPRIVATE void dgram_daytime() ;
  861. XPRIVATE void stream_chargen() ;
  862. XPRIVATE void dgram_chargen() ;
  863. XPRIVATE void stream_servers() ;
  864. XPRIVATE void stream_services() ;
  865. X
  866. X
  867. Xstatic struct builtin_service builtin_services[] =
  868. X    {
  869. X        { "echo",        SOCK_STREAM,    { stream_echo,         FORK      } },
  870. X        { "echo",        SOCK_DGRAM,        { dgram_echo,            NO_FORK    } },
  871. X        { "discard",    SOCK_STREAM,    { stream_discard,        FORK        } },
  872. X        { "discard",    SOCK_DGRAM,        { dgram_discard,        NO_FORK    } },
  873. X        { "time",        SOCK_STREAM,    { stream_time,            NO_FORK    } },
  874. X        { "time",        SOCK_DGRAM,        { dgram_time,            NO_FORK    } },
  875. X        { "daytime",    SOCK_STREAM,    { stream_daytime,        NO_FORK    } },
  876. X        { "daytime",    SOCK_DGRAM,        { dgram_daytime,        NO_FORK    } },
  877. X        { "chargen",    SOCK_STREAM,    { stream_chargen,        FORK        } },
  878. X        { "chargen",    SOCK_DGRAM,        { dgram_chargen,        NO_FORK    } },
  879. X        { "servers",    SOCK_STREAM,    { stream_servers,        FORK        } },
  880. X        { "services",    SOCK_STREAM,    { stream_services,    FORK        } },
  881. X        { NULL }
  882. X    } ;
  883. X
  884. X
  885. Xbuiltin_s *builtin_find( service_name, type )
  886. X    char    *service_name ;
  887. X    int    type ;
  888. X{
  889. X    register builtin_s    *bsp ;
  890. X    char                        *func = "builtin_find" ;
  891. X
  892. X    if ( bsp = builtin_lookup( builtin_services, service_name, type ) )
  893. X        return( bsp ) ;
  894. X    
  895. X    msg( LOG_ERR, func, "No such internal service: %s", service_name ) ;
  896. X    return( NULL ) ;
  897. X}
  898. X
  899. X
  900. Xbuiltin_s *builtin_lookup( services, service_name, type )
  901. X    struct builtin_service    services[] ;
  902. X    register char                *service_name ;
  903. X    register int                type ;
  904. X{
  905. X    register struct builtin_service *bsp ;
  906. X
  907. X    for ( bsp = services ; bsp->bs_name != NULL ; bsp++ )
  908. X        if ( EQ( bsp->bs_name, service_name ) && bsp->bs_socket_type == type )
  909. X            return( &bsp->bs_handle ) ;
  910. X    return( NULL ) ;
  911. X}
  912. X
  913. X
  914. X/*
  915. X * The rest of this file contains the functions that implement the 
  916. X * builtin services
  917. X */
  918. X
  919. X
  920. XPRIVATE void stream_echo( serp )
  921. X    struct server *serp ;
  922. X{
  923. X    char                buf[ BUFFER_SIZE ] ;
  924. X    register int    cc ;
  925. X    int                descriptor = SERVER_FD( serp ) ;
  926. X
  927. X    for ( ;; )
  928. X    {
  929. X        cc = read( descriptor, buf, sizeof( buf ) ) ;
  930. X        if ( cc == 0 )
  931. X            break ;
  932. X        if ( cc == -1 )
  933. X            if ( errno == EINTR )
  934. X                continue ;
  935. X            else
  936. X                break ;
  937. X
  938. X        if ( write_buf( descriptor, buf, cc ) == FAILED )
  939. X            break ;
  940. X    }
  941. X}
  942. X
  943. X
  944. XPRIVATE void dgram_echo( serp )
  945. X    struct server *serp ;
  946. X{
  947. X    char                        buf[ DATAGRAM_SIZE ] ;
  948. X    struct sockaddr_in    sin ;
  949. X    int                        sin_len ;
  950. X    int                        cc ;
  951. X    int                        descriptor = SERVER_FD( serp ) ;
  952. X
  953. X    cc = recvfrom( descriptor, buf, sizeof( buf ), 0, SA( &sin ), &sin_len ) ;
  954. X    if ( cc != -1 )
  955. X        (void) sendto( descriptor, buf, cc, 0, SA( &sin ), sizeof( sin ) ) ;
  956. X}
  957. X
  958. X
  959. XPRIVATE void stream_discard( serp )
  960. X    struct server *serp ;
  961. X{
  962. X    char                buf[ BUFFER_SIZE ] ;
  963. X    register int    cc ;
  964. X
  965. X    for ( ;; )
  966. X    {
  967. X        cc = read( SERVER_FD( serp ), buf, sizeof( buf ) ) ;
  968. X        if ( cc == 0 || cc == -1 && errno != EINTR )
  969. X            break ;
  970. X    }
  971. X}
  972. X
  973. X
  974. XPRIVATE void dgram_discard( serp )
  975. X    struct server *serp ;
  976. X{
  977. X    char buf[ 1 ] ;
  978. X
  979. X    (void) recv( SERVER_FD( serp ), buf, sizeof( buf ), 0 ) ;
  980. X}
  981. X
  982. X
  983. X
  984. X/*
  985. X * Generate the current time using the SMTP format:
  986. X *        02 FEB 1991 12:31:42 MST
  987. X *
  988. X * The result is placed in buf.
  989. X * buflen is a value-result parameter. It indicates the size of
  990. X * buf and on exit it has the length of the string placed in buf.
  991. X */
  992. XPRIVATE void daytime_protocol( buf, buflen )
  993. X    char    *buf ;
  994. X    int    *buflen ;
  995. X{
  996. X    static char *month_name[] =
  997. X        {
  998. X            "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  999. X            "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
  1000. X        } ;
  1001. X    time_t        now ;
  1002. X    struct tm    *tmp ;
  1003. X    int            size = *buflen ;
  1004. X    
  1005. X    (void) time( &now ) ;
  1006. X    tmp = localtime( &now ) ;
  1007. X    strx_print( buflen, buf, size,
  1008. X        "%02d %s %d %02d:%02d:%02d %s\r\n",
  1009. X            tmp->tm_mday, month_name[ tmp->tm_mon ], 1900 + tmp->tm_year,
  1010. X                tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tmp->tm_zone ) ;
  1011. X}
  1012. X
  1013. X
  1014. XPRIVATE void stream_daytime( serp )
  1015. X    struct server *serp ;
  1016. X{
  1017. X    char    time_buf[ BUFFER_SIZE ] ;
  1018. X    int    buflen = sizeof( time_buf ) ;
  1019. X
  1020. X    daytime_protocol( time_buf, &buflen ) ;
  1021. X    (void) write_buf( SERVER_FD( serp ), time_buf, buflen ) ;
  1022. X}
  1023. X
  1024. X
  1025. XPRIVATE void dgram_daytime( serp )
  1026. X    struct server *serp ;
  1027. X{
  1028. X    char                        time_buf[ BUFFER_SIZE ] ;
  1029. X    struct sockaddr_in    sin ;
  1030. X    int                        sin_len        = sizeof( sin ) ;
  1031. X    int                        buflen        = sizeof( time_buf ) ;
  1032. X    int                        descriptor    = SERVER_FD( serp ) ;
  1033. X
  1034. X    if ( recvfrom( descriptor, time_buf, sizeof( time_buf ), 0,
  1035. X                SA( &sin ), &sin_len ) == -1 )
  1036. X        return ;
  1037. X
  1038. X    daytime_protocol( time_buf, &buflen ) ;
  1039. X    
  1040. X    (void) sendto( descriptor, time_buf, buflen, 0, SA(&sin), sizeof( sin ) ) ;
  1041. X}
  1042. X
  1043. X
  1044. X#define TIME_OFFSET            2208988800
  1045. X
  1046. X/*
  1047. X * We always report the time using network-byte-order
  1048. X */
  1049. XPRIVATE void time_protocol( timep )
  1050. X    register unsigned long *timep ;
  1051. X{
  1052. X    time_t now ;
  1053. X
  1054. X    (void) time( &now ) ;
  1055. X    *timep = now + TIME_OFFSET ;
  1056. X    *timep = htonl( *timep ) ;
  1057. X}
  1058. X
  1059. X
  1060. XPRIVATE void stream_time( serp )
  1061. X    struct server *serp ;
  1062. X{
  1063. X    unsigned long now ;
  1064. X
  1065. X    time_protocol( &now ) ;
  1066. X    (void) write_buf( SERVER_FD( serp ), (char *) &now, sizeof( now ) ) ;
  1067. X}
  1068. X
  1069. X
  1070. XPRIVATE void dgram_time( serp )
  1071. X    struct server *serp ;
  1072. X{
  1073. X    char                        buf[ 1 ] ;
  1074. X    unsigned long            now ;
  1075. X    struct sockaddr_in    sin ;
  1076. X    int                        sin_len    = sizeof( sin ) ;
  1077. X    int                        fd            = SERVER_FD( serp ) ;
  1078. X
  1079. X    if ( recvfrom( fd, buf, sizeof( buf ), 0, SA( &sin ), &sin_len ) == -1 )
  1080. X        return ;
  1081. X
  1082. X    time_protocol( &now ) ;
  1083. X    (void) sendto( fd, (char *) &now, sizeof( now ), 0, SA( &sin ), sin_len ) ;
  1084. X}
  1085. X
  1086. X
  1087. X#define ASCII_PRINTABLE_CHARS                94
  1088. X#define LINE_LENGTH                            72
  1089. X
  1090. X#define RING_BUF_SIZE                        ASCII_PRINTABLE_CHARS + LINE_LENGTH
  1091. X
  1092. Xstatic char ring_buf[ RING_BUF_SIZE ] ;
  1093. Xstatic char *ring ;
  1094. X
  1095. X
  1096. X#define ASCII_START                ( ' ' + 1 )
  1097. X#define ASCII_END                    126
  1098. X
  1099. X#define min( a, b )                ((a)<(b) ? (a) : (b))
  1100. X
  1101. XPRIVATE char *generate_line( buf, len )
  1102. X    char    *buf ;
  1103. X    int    len ;
  1104. X{
  1105. X    int line_len = min( LINE_LENGTH, len-2 ) ;
  1106. X
  1107. X    if ( line_len < 0 )
  1108. X        return( NULL ) ;
  1109. X
  1110. X    if ( ring == NULL )
  1111. X    {
  1112. X        register char ch ;
  1113. X        register char *p ;
  1114. X
  1115. X        for ( p = ring_buf, ch = ASCII_START ;
  1116. X                            p < &ring_buf[ RING_BUF_SIZE ] ; p++ )
  1117. X        {
  1118. X            *p = ch++ ;
  1119. X            if ( ch == ASCII_END )
  1120. X                ch = ASCII_START ;
  1121. X        }
  1122. X        ring = ring_buf ;
  1123. X    }
  1124. X    (void) memcpy( buf, ring, line_len ) ;
  1125. X    buf[ line_len   ] = '\r' ;
  1126. X    buf[ line_len+1 ] = '\n' ;
  1127. X
  1128. X    ring++ ;
  1129. X    if ( &ring_buf[ RING_BUF_SIZE ] - ring < LINE_LENGTH )
  1130. X        ring = ring_buf ;
  1131. X    return( buf ) ;
  1132. X}
  1133. X
  1134. X
  1135. XPRIVATE void stream_chargen( serp )
  1136. X    struct server *serp ;
  1137. X{
  1138. X    char    line_buf[ LINE_LENGTH+2 ] ;
  1139. X    int    descriptor = SERVER_FD( serp ) ;
  1140. X
  1141. X    (void) shutdown( descriptor, 0 ) ;
  1142. X    for ( ;; )
  1143. X    {
  1144. X        if ( generate_line( line_buf, sizeof( line_buf ) ) == NULL )
  1145. X            break ;
  1146. X        if ( write_buf( descriptor, line_buf, sizeof( line_buf ) ) == FAILED )
  1147. X            break ;
  1148. X    }
  1149. X}
  1150. X
  1151. X
  1152. XPRIVATE void dgram_chargen( serp )
  1153. X    struct server *serp ;
  1154. X{
  1155. X    char                        buf[ BUFFER_SIZE ] ;
  1156. X    register char            *p ;
  1157. X    register int            len ;
  1158. X    struct sockaddr_in    sin ;
  1159. X    int                        sin_len    = sizeof( sin ) ;
  1160. X    int                        fd            = SERVER_FD( serp ) ;
  1161. X    register int            left        = sizeof( buf ) ;
  1162. X
  1163. X    if ( recvfrom( fd, buf, sizeof( buf ), 0, SA( &sin ), &sin_len ) == -1 )
  1164. X        return ;
  1165. X
  1166. X#if BUFFER_SIZE < LINE_LENGTH+2
  1167. X    bad_variable = 1 ;        /* this will cause a compilation error */
  1168. X#endif
  1169. X
  1170. X    for ( p = buf ; left > 2 ; left -= len, p += len )
  1171. X    {
  1172. X        len = min( LINE_LENGTH+2, left ) ;
  1173. X        if ( generate_line( p, len ) == NULL )
  1174. X            break ;
  1175. X    }
  1176. X    (void) sendto( fd, buf, p-buf, 0, SA( &sin ), sin_len ) ;
  1177. X}
  1178. X
  1179. X
  1180. XPRIVATE void stream_servers( this_serp )
  1181. X    struct server *this_serp ;
  1182. X{
  1183. X    register unsigned        u ;
  1184. X    int                        descriptor = SERVER_FD( this_serp ) ;
  1185. X
  1186. X    for ( u = 0 ; u < pset_count( SERVERS( ps ) ) ; u++ )
  1187. X    {
  1188. X        struct server *serp = SERP( pset_pointer( SERVERS( ps ), u ) ) ;
  1189. X
  1190. X        /*
  1191. X         * We cannot report any useful information about this server because
  1192. X         * the data in the server struct are filled by the parent.
  1193. X         */
  1194. X        if ( serp == this_serp )
  1195. X            continue ;
  1196. X
  1197. X        server_dump( serp, descriptor ) ;
  1198. X    }
  1199. X}
  1200. X
  1201. X
  1202. XPRIVATE void stream_services( serp )
  1203. X    struct server *serp ;
  1204. X{
  1205. X    register unsigned u ;
  1206. X    int fd = SERVER_FD( serp ) ;
  1207. X
  1208. X    for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ )
  1209. X    {
  1210. X        int cc ;
  1211. X        char buf[ BUFFER_SIZE ] ;
  1212. X        struct service_config *scp ;
  1213. X        
  1214. X        scp = SVC_CONF( SP( pset_pointer( SERVICES( ps ), u ) ) ) ;
  1215. X
  1216. X        strx_print( &cc, buf, sizeof( buf ), "%s %s %d\n",
  1217. X            SC_NAME( scp ), SC_PROTONAME( scp ), SC_PORT( scp ) ) ;
  1218. X            
  1219. X        if ( write_buf( fd, buf, cc ) == FAILED )
  1220. X            break ;
  1221. X    }
  1222. X}
  1223. X
  1224. END_OF_FILE
  1225. if test 8896 -ne `wc -c <'xinetd/builtins.c'`; then
  1226.     echo shar: \"'xinetd/builtins.c'\" unpacked with wrong size!
  1227. fi
  1228. # end of 'xinetd/builtins.c'
  1229. fi
  1230. echo shar: End of archive 18 \(of 31\).
  1231. cp /dev/null ark18isdone
  1232. MISSING=""
  1233. 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 24 25 26 27 28 29 30 31 ; do
  1234.     if test ! -f ark${I}isdone ; then
  1235.     MISSING="${MISSING} ${I}"
  1236.     fi
  1237. done
  1238. if test "${MISSING}" = "" ; then
  1239.     echo You have unpacked all 31 archives.
  1240.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1241. else
  1242.     echo You still need to unpack the following archives:
  1243.     echo "        " ${MISSING}
  1244. fi
  1245. ##  End of shell archive.
  1246. exit 0
  1247.