RTEMS CPU Kit with SuperCore  4.10.99.0
rtems/libio_.h
Go to the documentation of this file.
00001 
00009 /*
00010  *  COPYRIGHT (c) 1989-2011.
00011  *  On-Line Applications Research Corporation (OAR).
00012  *
00013  *  Modifications to support reference counting in the file system are
00014  *  Copyright (c) 2012 embedded brains GmbH.
00015  *
00016  *  The license and distribution terms for this file may be
00017  *  found in the file LICENSE in this distribution or at
00018  *  http://www.rtems.org/license/LICENSE.
00019  */
00020 
00021 #ifndef _RTEMS_RTEMS_LIBIO__H
00022 #define _RTEMS_RTEMS_LIBIO__H
00023 
00024 #include <sys/uio.h>
00025 #include <errno.h>
00026 #include <limits.h>
00027 #include <pthread.h>
00028 
00029 #include <rtems.h>
00030 #include <rtems/libio.h>
00031 #include <rtems/seterr.h>
00032 
00033 #ifdef __cplusplus
00034 extern "C" {
00035 #endif
00036 
00045 #define RTEMS_FILESYSTEM_SYMLOOP_MAX 32
00046 
00047 /*
00048  * Not defined in newlib so provide here. Users should use dup2 and
00049  * not this non-portable fcntl command. Provided here to allow the
00050  * RTEMS implementation to work.
00051  */
00052 #define F_DUP2FD 20
00053 
00054 /*
00055  *  Semaphore to protect the io table
00056  */
00057 
00058 #define RTEMS_LIBIO_SEM         rtems_build_name('L', 'B', 'I', 'O')
00059 #define RTEMS_LIBIO_IOP_SEM(n)  rtems_build_name('L', 'B', 'I', n)
00060 
00061 extern rtems_id                          rtems_libio_semaphore;
00062 
00063 /*
00064  *  File descriptor Table Information
00065  */
00066 
00067 extern uint32_t        rtems_libio_number_iops;
00068 extern rtems_libio_t  *rtems_libio_iops;
00069 extern rtems_libio_t  *rtems_libio_last_iop;
00070 extern rtems_libio_t *rtems_libio_iop_freelist;
00071 
00072 extern const rtems_filesystem_file_handlers_r rtems_filesystem_null_handlers;
00073 
00074 extern rtems_filesystem_mount_table_entry_t rtems_filesystem_null_mt_entry;
00075 
00091 extern rtems_filesystem_global_location_t rtems_filesystem_global_location_null;
00092 
00093 /*
00094  *  rtems_libio_iop
00095  *
00096  *  Macro to return the file descriptor pointer.
00097  */
00098 
00099 #define rtems_libio_iop(_fd) \
00100   ((((uint32_t)(_fd)) < rtems_libio_number_iops) ? \
00101          &rtems_libio_iops[_fd] : 0)
00102 
00103 /*
00104  *  rtems_libio_iop_to_descriptor
00105  *
00106  *  Macro to convert an internal file descriptor pointer (iop) into
00107  *  the integer file descriptor used by the "section 2" system calls.
00108  */
00109 
00110 #define rtems_libio_iop_to_descriptor(_iop) \
00111    ((!(_iop)) ? -1 : (_iop - rtems_libio_iops))
00112 
00113 /*
00114  *  rtems_libio_check_is_open
00115  *
00116  *  Macro to check if a file descriptor is actually open.
00117  */
00118 
00119 #define rtems_libio_check_is_open(_iop) \
00120   do {                                               \
00121       if (((_iop)->flags & LIBIO_FLAGS_OPEN) == 0) { \
00122           errno = EBADF;                             \
00123           return -1;                                 \
00124       }                                              \
00125   } while (0)
00126 
00127 /*
00128  *  rtems_libio_check_fd
00129  *
00130  *  Macro to check if a file descriptor number is valid.
00131  */
00132 
00133 #define rtems_libio_check_fd(_fd) \
00134   do {                                                     \
00135       if ((uint32_t) (_fd) >= rtems_libio_number_iops) {   \
00136           errno = EBADF;                                   \
00137           return -1;                                       \
00138       }                                                    \
00139   } while (0)
00140 
00141 /*
00142  *  rtems_libio_check_buffer
00143  *
00144  *  Macro to check if a buffer pointer is valid.
00145  */
00146 
00147 #define rtems_libio_check_buffer(_buffer) \
00148   do {                                    \
00149       if ((_buffer) == 0) {               \
00150           errno = EINVAL;                 \
00151           return -1;                      \
00152       }                                   \
00153   } while (0)
00154 
00155 /*
00156  *  rtems_libio_check_count
00157  *
00158  *  Macro to check if a count or length is valid.
00159  */
00160 
00161 #define rtems_libio_check_count(_count) \
00162   do {                                  \
00163       if ((_count) == 0) {              \
00164           return 0;                     \
00165       }                                 \
00166   } while (0)
00167 
00168 /*
00169  *  rtems_libio_check_permissions_with_error
00170  *
00171  *  Macro to check if a file descriptor is open for this operation.
00172  *  On failure, return the user specified error.
00173  */
00174 
00175 #define rtems_libio_check_permissions_with_error(_iop, _flag, _errno) \
00176   do {                                                      \
00177       if (((_iop)->flags & (_flag)) == 0) {                 \
00178             rtems_set_errno_and_return_minus_one( _errno ); \
00179             return -1;                                      \
00180       }                                                     \
00181   } while (0)
00182 
00183 /*
00184  *  rtems_libio_check_permissions
00185  *
00186  *  Macro to check if a file descriptor is open for this operation.
00187  *  On failure, return EINVAL
00188  */
00189 
00190 #define rtems_libio_check_permissions(_iop, _flag) \
00191    rtems_libio_check_permissions_with_error(_iop, _flag, EINVAL )
00192 
00203 void rtems_filesystem_location_clone(
00204   rtems_filesystem_location_info_t *clone,
00205   const rtems_filesystem_location_info_t *master
00206 );
00207 
00219 rtems_filesystem_node_types_t rtems_filesystem_node_type(
00220   const rtems_filesystem_location_info_t *loc
00221 );
00222 
00235 void rtems_filesystem_location_free( rtems_filesystem_location_info_t *loc );
00236 
00237 /*
00238  *  External structures
00239  */
00240 #include <rtems/userenv.h>
00241 
00242 void rtems_libio_free_user_env( void *env );
00243 
00244 extern pthread_key_t rtems_current_user_env_key;
00245 
00246 static inline void rtems_libio_lock( void )
00247 {
00248   rtems_semaphore_obtain( rtems_libio_semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
00249 }
00250 
00251 static inline void rtems_libio_unlock( void )
00252 {
00253   rtems_semaphore_release( rtems_libio_semaphore );
00254 }
00255 
00256 static inline void rtems_filesystem_mt_lock( void )
00257 {
00258   rtems_libio_lock();
00259 }
00260 
00261 static inline void rtems_filesystem_mt_unlock( void )
00262 {
00263   rtems_libio_unlock();
00264 }
00265 
00266 extern rtems_interrupt_lock rtems_filesystem_mt_entry_lock_control;
00267 
00268 #define rtems_filesystem_mt_entry_declare_lock_context( ctx ) \
00269   rtems_interrupt_lock_context ctx
00270 
00271 #define rtems_filesystem_mt_entry_lock( ctx ) \
00272   rtems_interrupt_lock_acquire( &rtems_filesystem_mt_entry_lock_control, &ctx )
00273 
00274 #define rtems_filesystem_mt_entry_unlock( ctx ) \
00275   rtems_interrupt_lock_release( &rtems_filesystem_mt_entry_lock_control, &ctx )
00276 
00277 static inline void rtems_filesystem_instance_lock(
00278   const rtems_filesystem_location_info_t *loc
00279 )
00280 {
00281   const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
00282 
00283   (*mt_entry->ops->lock_h)( mt_entry );
00284 }
00285 
00286 static inline void rtems_filesystem_instance_unlock(
00287   const rtems_filesystem_location_info_t *loc
00288 )
00289 {
00290   const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
00291 
00292   (*mt_entry->ops->unlock_h)( mt_entry );
00293 }
00294 
00295 /*
00296  *  File Descriptor Routine Prototypes
00297  */
00298 
00303 rtems_libio_t *rtems_libio_allocate(void);
00304 
00308 uint32_t rtems_libio_fcntl_flags( int fcntl_flags );
00309 
00313 int rtems_libio_to_fcntl_flags( uint32_t flags );
00314 
00319 void rtems_libio_free(
00320   rtems_libio_t *iop
00321 );
00322 
00323 /*
00324  *  File System Routine Prototypes
00325  */
00326 
00327 rtems_filesystem_location_info_t *
00328 rtems_filesystem_eval_path_start(
00329   rtems_filesystem_eval_path_context_t *ctx,
00330   const char *path,
00331   int eval_flags
00332 );
00333 
00334 rtems_filesystem_location_info_t *
00335 rtems_filesystem_eval_path_start_with_parent(
00336   rtems_filesystem_eval_path_context_t *ctx,
00337   const char *path,
00338   int eval_flags,
00339   rtems_filesystem_location_info_t *parentloc,
00340   int parent_eval_flags
00341 );
00342 
00343 rtems_filesystem_location_info_t *
00344 rtems_filesystem_eval_path_start_with_root_and_current(
00345   rtems_filesystem_eval_path_context_t *ctx,
00346   const char *path,
00347   int eval_flags,
00348   rtems_filesystem_global_location_t *const *global_root_ptr,
00349   rtems_filesystem_global_location_t *const *global_current_ptr
00350 );
00351 
00352 void rtems_filesystem_eval_path_continue(
00353   rtems_filesystem_eval_path_context_t *ctx
00354 );
00355 
00356 void rtems_filesystem_eval_path_cleanup(
00357   rtems_filesystem_eval_path_context_t *ctx
00358 );
00359 
00360 void rtems_filesystem_eval_path_recursive(
00361   rtems_filesystem_eval_path_context_t *ctx,
00362   const char *path,
00363   size_t pathlen
00364 );
00365 
00366 void rtems_filesystem_eval_path_cleanup_with_parent(
00367   rtems_filesystem_eval_path_context_t *ctx,
00368   rtems_filesystem_location_info_t *parentloc
00369 );
00370 
00384 void rtems_filesystem_eval_path_restart(
00385   rtems_filesystem_eval_path_context_t *ctx,
00386   rtems_filesystem_global_location_t **newstartloc_ptr
00387 );
00388 
00389 typedef enum {
00390   RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE,
00391   RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE,
00392   RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY
00393 } rtems_filesystem_eval_path_generic_status;
00394 
00406 typedef bool (*rtems_filesystem_eval_path_is_directory)(
00407   rtems_filesystem_eval_path_context_t *ctx,
00408   void *arg
00409 );
00410 
00423 typedef rtems_filesystem_eval_path_generic_status
00424 (*rtems_filesystem_eval_path_eval_token)(
00425   rtems_filesystem_eval_path_context_t *ctx,
00426   void *arg,
00427   const char *token,
00428   size_t tokenlen
00429 );
00430 
00431 typedef struct {
00432   rtems_filesystem_eval_path_is_directory is_directory;
00433   rtems_filesystem_eval_path_eval_token eval_token;
00434 } rtems_filesystem_eval_path_generic_config;
00435 
00436 void rtems_filesystem_eval_path_generic(
00437   rtems_filesystem_eval_path_context_t *ctx,
00438   void *arg,
00439   const rtems_filesystem_eval_path_generic_config *config
00440 );
00441 
00442 void rtems_filesystem_initialize(void);
00443 
00457 rtems_filesystem_location_info_t *rtems_filesystem_location_copy(
00458   rtems_filesystem_location_info_t *dst,
00459   const rtems_filesystem_location_info_t *src
00460 );
00461 
00462 static inline rtems_filesystem_location_info_t *
00463 rtems_filesystem_location_initialize_to_null(
00464   rtems_filesystem_location_info_t *loc
00465 )
00466 {
00467   return rtems_filesystem_location_copy(
00468     loc,
00469     &rtems_filesystem_global_location_null.location
00470   );
00471 }
00472 
00473 rtems_filesystem_global_location_t *
00474 rtems_filesystem_location_transform_to_global(
00475   rtems_filesystem_location_info_t *loc
00476 );
00477 
00485 void rtems_filesystem_global_location_assign(
00486   rtems_filesystem_global_location_t **lhs_global_loc_ptr,
00487   rtems_filesystem_global_location_t *rhs_global_loc
00488 );
00489 
00509 rtems_filesystem_global_location_t *rtems_filesystem_global_location_obtain(
00510   rtems_filesystem_global_location_t *const *global_loc_ptr
00511 );
00512 
00528 void rtems_filesystem_global_location_release(
00529   rtems_filesystem_global_location_t *global_loc
00530 );
00531 
00532 void rtems_filesystem_location_detach(
00533   rtems_filesystem_location_info_t *detach
00534 );
00535 
00536 void rtems_filesystem_location_copy_and_detach(
00537   rtems_filesystem_location_info_t *copy,
00538   rtems_filesystem_location_info_t *detach
00539 );
00540 
00541 static inline rtems_filesystem_global_location_t *
00542 rtems_filesystem_global_location_obtain_null(void)
00543 {
00544   rtems_filesystem_global_location_t *global_loc = NULL;
00545 
00546   return rtems_filesystem_global_location_obtain( &global_loc );
00547 }
00548 
00549 static inline bool rtems_filesystem_location_is_null(
00550   const rtems_filesystem_location_info_t *loc
00551 )
00552 {
00553   return loc->handlers == &rtems_filesystem_null_handlers;
00554 }
00555 
00556 static inline bool rtems_filesystem_global_location_is_null(
00557   const rtems_filesystem_global_location_t *global_loc
00558 )
00559 {
00560   return rtems_filesystem_location_is_null( &global_loc->location );
00561 }
00562 
00563 static inline void rtems_filesystem_location_error(
00564   const rtems_filesystem_location_info_t *loc,
00565   int eno
00566 )
00567 {
00568   if ( !rtems_filesystem_location_is_null( loc ) ) {
00569     errno = eno;
00570   }
00571 }
00572 
00573 int rtems_filesystem_mknod(
00574   const rtems_filesystem_location_info_t *parentloc,
00575   const char *name,
00576   size_t namelen,
00577   mode_t mode,
00578   dev_t dev
00579 );
00580 
00581 int rtems_filesystem_chdir( rtems_filesystem_location_info_t *loc );
00582 
00583 int rtems_filesystem_chmod(
00584   const rtems_filesystem_location_info_t *loc,
00585   mode_t mode
00586 );
00587 
00588 int rtems_filesystem_chown(
00589   const rtems_filesystem_location_info_t *loc,
00590   uid_t owner,
00591   gid_t group
00592 );
00593 
00594 static inline bool rtems_filesystem_is_ready_for_unmount(
00595   rtems_filesystem_mount_table_entry_t *mt_entry
00596 )
00597 {
00598   bool ready = !mt_entry->mounted
00599     && rtems_chain_has_only_one_node( &mt_entry->location_chain )
00600     && mt_entry->mt_fs_root->reference_count == 1;
00601 
00602   if ( ready ) {
00603     rtems_chain_initialize_empty( &mt_entry->location_chain );
00604   }
00605 
00606   return ready;
00607 }
00608 
00609 static inline void rtems_filesystem_location_add_to_mt_entry(
00610   rtems_filesystem_location_info_t *loc
00611 )
00612 {
00613   rtems_filesystem_mt_entry_declare_lock_context( lock_context );
00614 
00615   rtems_filesystem_mt_entry_lock( lock_context );
00616   rtems_chain_append_unprotected(
00617     &loc->mt_entry->location_chain,
00618     &loc->mt_entry_node
00619   );
00620   rtems_filesystem_mt_entry_unlock( lock_context );
00621 }
00622 
00623 void rtems_filesystem_location_remove_from_mt_entry(
00624   rtems_filesystem_location_info_t *loc
00625 );
00626 
00627 void rtems_filesystem_do_unmount(
00628   rtems_filesystem_mount_table_entry_t *mt_entry
00629 );
00630 
00631 static inline bool rtems_filesystem_location_is_instance_root(
00632   const rtems_filesystem_location_info_t *loc
00633 )
00634 {
00635   const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
00636 
00637   return (*mt_entry->ops->are_nodes_equal_h)(
00638     loc,
00639     &mt_entry->mt_fs_root->location
00640   );
00641 }
00642 
00643 static inline const char *rtems_filesystem_eval_path_get_path(
00644   rtems_filesystem_eval_path_context_t *ctx
00645 )
00646 {
00647   return ctx->path;
00648 }
00649 
00650 static inline size_t rtems_filesystem_eval_path_get_pathlen(
00651   rtems_filesystem_eval_path_context_t *ctx
00652 )
00653 {
00654   return ctx->pathlen;
00655 }
00656 
00657 static inline void rtems_filesystem_eval_path_set_path(
00658   rtems_filesystem_eval_path_context_t *ctx,
00659   const char *path,
00660   size_t pathlen
00661 )
00662 {
00663   ctx->path = path;
00664   ctx->pathlen = pathlen;
00665 }
00666 
00667 static inline void rtems_filesystem_eval_path_clear_path(
00668   rtems_filesystem_eval_path_context_t *ctx
00669 )
00670 {
00671   ctx->pathlen = 0;
00672 }
00673 
00674 static inline const char *rtems_filesystem_eval_path_get_token(
00675   rtems_filesystem_eval_path_context_t *ctx
00676 )
00677 {
00678   return ctx->token;
00679 }
00680 
00681 static inline size_t rtems_filesystem_eval_path_get_tokenlen(
00682   rtems_filesystem_eval_path_context_t *ctx
00683 )
00684 {
00685   return ctx->tokenlen;
00686 }
00687 
00688 static inline void rtems_filesystem_eval_path_set_token(
00689   rtems_filesystem_eval_path_context_t *ctx,
00690   const char *token,
00691   size_t tokenlen
00692 )
00693 {
00694   ctx->token = token;
00695   ctx->tokenlen = tokenlen;
00696 }
00697 
00698 static inline void rtems_filesystem_eval_path_clear_token(
00699   rtems_filesystem_eval_path_context_t *ctx
00700 )
00701 {
00702   ctx->tokenlen = 0;
00703 }
00704 
00705 static inline void rtems_filesystem_eval_path_put_back_token(
00706   rtems_filesystem_eval_path_context_t *ctx
00707 )
00708 {
00709   size_t tokenlen = ctx->tokenlen;
00710 
00711   ctx->path -= tokenlen;
00712   ctx->pathlen += tokenlen;
00713   ctx->tokenlen = 0;
00714 }
00715 
00716 void rtems_filesystem_eval_path_eat_delimiter(
00717   rtems_filesystem_eval_path_context_t *ctx
00718 );
00719 
00720 void rtems_filesystem_eval_path_next_token(
00721   rtems_filesystem_eval_path_context_t *ctx
00722 );
00723 
00724 static inline void rtems_filesystem_eval_path_get_next_token(
00725   rtems_filesystem_eval_path_context_t *ctx,
00726   const char **token,
00727   size_t *tokenlen
00728 )
00729 {
00730   rtems_filesystem_eval_path_next_token(ctx);
00731   *token = ctx->token;
00732   *tokenlen = ctx->tokenlen;
00733 }
00734 
00735 static inline rtems_filesystem_location_info_t *
00736 rtems_filesystem_eval_path_get_currentloc(
00737   rtems_filesystem_eval_path_context_t *ctx
00738 )
00739 {
00740   return &ctx->currentloc;
00741 }
00742 
00743 static inline bool rtems_filesystem_eval_path_has_path(
00744   const rtems_filesystem_eval_path_context_t *ctx
00745 )
00746 {
00747   return ctx->pathlen > 0;
00748 }
00749 
00750 static inline bool rtems_filesystem_eval_path_has_token(
00751   const rtems_filesystem_eval_path_context_t *ctx
00752 )
00753 {
00754   return ctx->tokenlen > 0;
00755 }
00756 
00757 static inline int rtems_filesystem_eval_path_get_flags(
00758   const rtems_filesystem_eval_path_context_t *ctx
00759 )
00760 {
00761   return ctx->flags;
00762 }
00763 
00764 static inline void rtems_filesystem_eval_path_set_flags(
00765   rtems_filesystem_eval_path_context_t *ctx,
00766   int flags
00767 )
00768 {
00769   ctx->flags = flags;
00770 }
00771 
00772 static inline void rtems_filesystem_eval_path_clear_and_set_flags(
00773   rtems_filesystem_eval_path_context_t *ctx,
00774   int clear,
00775   int set
00776 )
00777 {
00778   int flags = ctx->flags;
00779 
00780   flags &= ~clear;
00781   flags |= set;
00782 
00783   ctx->flags = flags;
00784 }
00785 
00786 static inline void rtems_filesystem_eval_path_extract_currentloc(
00787   rtems_filesystem_eval_path_context_t *ctx,
00788   rtems_filesystem_location_info_t *get
00789 )
00790 {
00791   rtems_filesystem_location_copy_and_detach(
00792     get,
00793     &ctx->currentloc
00794   );
00795 }
00796 
00797 void rtems_filesystem_eval_path_error(
00798   rtems_filesystem_eval_path_context_t *ctx,
00799   int eno
00800 );
00801 
00808 int rtems_filesystem_location_exists_in_same_instance_as(
00809   const rtems_filesystem_location_info_t *a,
00810   const rtems_filesystem_location_info_t *b
00811 );
00812 
00813 bool rtems_filesystem_check_access(
00814   int eval_flags,
00815   mode_t node_mode,
00816   uid_t node_uid,
00817   gid_t node_gid
00818 );
00819 
00820 bool rtems_filesystem_eval_path_check_access(
00821   rtems_filesystem_eval_path_context_t *ctx,
00822   int eval_flags,
00823   mode_t node_mode,
00824   uid_t node_uid,
00825   gid_t node_gid
00826 );
00827 
00828 static inline bool rtems_filesystem_is_delimiter(char c)
00829 {
00830   return c == '/' || c == '\\';
00831 }
00832 
00833 static inline bool rtems_filesystem_is_current_directory(
00834   const char *token,
00835   size_t tokenlen
00836 )
00837 {
00838   return tokenlen == 1 && token [0] == '.';
00839 }
00840 
00841 static inline bool rtems_filesystem_is_parent_directory(
00842   const char *token,
00843   size_t tokenlen
00844 )
00845 {
00846   return tokenlen == 2 && token [0] == '.' && token [1] == '.';
00847 }
00848 
00849 static inline ssize_t rtems_libio_iovec_eval(
00850   int fd,
00851   const struct iovec *iov,
00852   int iovcnt,
00853   uint32_t flags,
00854   rtems_libio_t **iopp
00855 )
00856 {
00857   ssize_t        total;
00858   int            v;
00859   rtems_libio_t *iop;
00860 
00861   rtems_libio_check_fd( fd );
00862   iop = rtems_libio_iop( fd );
00863   rtems_libio_check_is_open( iop );
00864   rtems_libio_check_permissions_with_error( iop, flags, EBADF );
00865 
00866   *iopp = iop;
00867 
00868   /*
00869    *  Argument validation on IO vector
00870    */
00871   if ( iov == NULL )
00872     rtems_set_errno_and_return_minus_one( EINVAL );
00873 
00874   if ( iovcnt <= 0 )
00875     rtems_set_errno_and_return_minus_one( EINVAL );
00876 
00877   if ( iovcnt > IOV_MAX )
00878     rtems_set_errno_and_return_minus_one( EINVAL );
00879 
00880   /*
00881    *  OpenGroup says that you are supposed to return EINVAL if the
00882    *  sum of the iov_len values in the iov array would overflow a
00883    *  ssize_t.
00884    */
00885   total = 0;
00886   for ( v = 0 ; v < iovcnt ; ++v ) {
00887     size_t len = iov[ v ].iov_len;
00888 
00889     if ( len > ( size_t ) ( SSIZE_MAX - total ) ) {
00890       rtems_set_errno_and_return_minus_one( EINVAL );
00891     }
00892 
00893     total += ( ssize_t ) len;
00894 
00895     if ( iov[ v ].iov_base == NULL && len != 0 ) {
00896       rtems_set_errno_and_return_minus_one( EINVAL );
00897     }
00898   }
00899 
00900   return total;
00901 }
00902 
00905 #ifdef __cplusplus
00906 }
00907 #endif
00908 
00909 #endif
00910 /* end of include file */