source: rtems/cpukit/libcsupport/include/rtems/libio_.h @ da154e14

4.11
Last change on this file since da154e14 was da154e14, checked in by Sebastian Huber <sebastian.huber@…>, on May 14, 2012 at 2:55:41 PM

Filesystem: Move operations to mount table entry

The scope of the file system operations is the file system instance.
The scope of the file system node handlers is the file location. The
benefit of moving the operations to the mount table entry is a size
reduction of the file location (rtems_filesystem_location_info_t). The
code size is slightly increased due to additional load instructions.

Restructure rtems_filesystem_mount_table_entry_t to improve cache
efficiency.

  • Property mode set to 100644
File size: 20.2 KB
Line 
1/**
2 * @file rtems/libio_.h
3 *
4 * This file is the libio internal interface.
5 */
6
7/*
8 *  COPYRIGHT (c) 1989-2011.
9 *  On-Line Applications Research Corporation (OAR).
10 *
11 *  Modifications to support reference counting in the file system are
12 *  Copyright (c) 2012 embedded brains GmbH.
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.com/license/LICENSE.
17 */
18
19#ifndef _RTEMS_RTEMS_LIBIO__H
20#define _RTEMS_RTEMS_LIBIO__H
21
22#include <errno.h>
23
24#include <rtems.h>
25#include <rtems/libio.h>
26#include <rtems/seterr.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32#define RTEMS_FILESYSTEM_SYMLOOP_MAX 32
33
34/*
35 *  Semaphore to protect the io table
36 */
37
38#define RTEMS_LIBIO_SEM         rtems_build_name('L', 'B', 'I', 'O')
39#define RTEMS_LIBIO_IOP_SEM(n)  rtems_build_name('L', 'B', 'I', n)
40
41extern rtems_id                          rtems_libio_semaphore;
42
43/*
44 *  File descriptor Table Information
45 */
46
47extern uint32_t        rtems_libio_number_iops;
48extern rtems_libio_t  *rtems_libio_iops;
49extern rtems_libio_t  *rtems_libio_last_iop;
50extern rtems_libio_t *rtems_libio_iop_freelist;
51
52extern const rtems_filesystem_file_handlers_r rtems_filesystem_null_handlers;
53
54extern rtems_filesystem_mount_table_entry_t rtems_filesystem_null_mt_entry;
55
56/**
57 * @brief The global null location.
58 *
59 * Every operation and the open and fstat handlers of this location returns an
60 * error status.  The errno is not touched by these operations and handlers.
61 * The purpose of this location is to deliver the error return status for a
62 * previous error condition which must set the errno accordingly.
63 *
64 * The usage of this null location instead of the NULL pointer eliminates a lot
65 * of branches.
66 *
67 * The user environment root and current directory are statically initialized
68 * with the null location.  Due to that all file system services are in a
69 * defined state even if no root file system was mounted.
70 */
71extern rtems_filesystem_global_location_t rtems_filesystem_global_location_null;
72
73/*
74 *  rtems_libio_iop
75 *
76 *  Macro to return the file descriptor pointer.
77 */
78
79#define rtems_libio_iop(_fd) \
80  ((((uint32_t)(_fd)) < rtems_libio_number_iops) ? \
81         &rtems_libio_iops[_fd] : 0)
82
83/*
84 *  rtems_libio_iop_to_descriptor
85 *
86 *  Macro to convert an internal file descriptor pointer (iop) into
87 *  the integer file descriptor used by the "section 2" system calls.
88 */
89
90#define rtems_libio_iop_to_descriptor(_iop) \
91   ((!(_iop)) ? -1 : (_iop - rtems_libio_iops))
92
93/*
94 *  rtems_libio_check_is_open
95 *
96 *  Macro to check if a file descriptor is actually open.
97 */
98
99#define rtems_libio_check_is_open(_iop) \
100  do {                                               \
101      if (((_iop)->flags & LIBIO_FLAGS_OPEN) == 0) { \
102          errno = EBADF;                             \
103          return -1;                                 \
104      }                                              \
105  } while (0)
106
107/*
108 *  rtems_libio_check_fd
109 *
110 *  Macro to check if a file descriptor number is valid.
111 */
112
113#define rtems_libio_check_fd(_fd) \
114  do {                                                     \
115      if ((uint32_t) (_fd) >= rtems_libio_number_iops) {   \
116          errno = EBADF;                                   \
117          return -1;                                       \
118      }                                                    \
119  } while (0)
120
121/*
122 *  rtems_libio_check_buffer
123 *
124 *  Macro to check if a buffer pointer is valid.
125 */
126
127#define rtems_libio_check_buffer(_buffer) \
128  do {                                    \
129      if ((_buffer) == 0) {               \
130          errno = EINVAL;                 \
131          return -1;                      \
132      }                                   \
133  } while (0)
134
135/*
136 *  rtems_libio_check_count
137 *
138 *  Macro to check if a count or length is valid.
139 */
140
141#define rtems_libio_check_count(_count) \
142  do {                                  \
143      if ((_count) == 0) {              \
144          return 0;                     \
145      }                                 \
146  } while (0)
147
148/*
149 *  rtems_libio_check_permissions_with_error
150 *
151 *  Macro to check if a file descriptor is open for this operation.
152 *  On failure, return the user specified error.
153 */
154
155#define rtems_libio_check_permissions_with_error(_iop, _flag, _errno) \
156  do {                                                      \
157      if (((_iop)->flags & (_flag)) == 0) {                 \
158            rtems_set_errno_and_return_minus_one( _errno ); \
159            return -1;                                      \
160      }                                                     \
161  } while (0)
162
163/*
164 *  rtems_libio_check_permissions
165 *
166 *  Macro to check if a file descriptor is open for this operation.
167 *  On failure, return EINVAL
168 */
169
170#define rtems_libio_check_permissions(_iop, _flag) \
171   rtems_libio_check_permissions_with_error(_iop, _flag, EINVAL )
172
173/**
174 * @brief Clones a node.
175 *
176 * The caller must hold the file system instance lock.
177 *
178 * @param[out] clone The cloned location.
179 * @param[in] master The master location.
180 *
181 * @see rtems_filesystem_instance_lock().
182 */
183void rtems_filesystem_location_clone(
184  rtems_filesystem_location_info_t *clone,
185  const rtems_filesystem_location_info_t *master
186);
187
188/**
189 * @brief Returns the type of a node.
190 *
191 * This function obtains and releases the file system instance lock.
192 *
193 * @param[in] loc The location of the node.
194 *
195 * @return The node type.
196 *
197 * @see rtems_filesystem_instance_lock().
198 */
199rtems_filesystem_node_types_t rtems_filesystem_node_type(
200  const rtems_filesystem_location_info_t *loc
201);
202
203/**
204 * @brief Releases all resources of a location.
205 *
206 * This function may block on a mutex and may complete an unmount process.
207 *
208 * @param[in] loc The location to free.
209 *
210 * @note The file system root location is released by the file system instance
211 * destruction handler (see @ref rtems_filesystem_fsunmount_me_t).
212 *
213 * @see rtems_filesystem_freenode_t.
214 */
215void rtems_filesystem_location_free( rtems_filesystem_location_info_t *loc );
216
217/*
218 *  External structures
219 */
220#include <rtems/userenv.h>
221
222static inline void rtems_libio_lock( void )
223{
224  rtems_semaphore_obtain( rtems_libio_semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
225}
226
227static inline void rtems_libio_unlock( void )
228{
229  rtems_semaphore_release( rtems_libio_semaphore );
230}
231
232static inline void rtems_filesystem_mt_lock( void )
233{
234  rtems_libio_lock();
235}
236
237static inline void rtems_filesystem_mt_unlock( void )
238{
239  rtems_libio_unlock();
240}
241
242#define rtems_filesystem_mt_entry_declare_lock_context( ctx ) \
243  rtems_interrupt_level ctx
244
245#define rtems_filesystem_mt_entry_lock( ctx ) rtems_interrupt_disable( ctx )
246
247#define rtems_filesystem_mt_entry_unlock( ctx ) rtems_interrupt_enable( ctx )
248
249static inline void rtems_filesystem_instance_lock(
250  const rtems_filesystem_location_info_t *loc
251)
252{
253  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
254
255  (*mt_entry->ops->lock_h)( mt_entry );
256}
257
258static inline void rtems_filesystem_instance_unlock(
259  const rtems_filesystem_location_info_t *loc
260)
261{
262  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
263
264  (*mt_entry->ops->unlock_h)( mt_entry );
265}
266
267/*
268 *  File Descriptor Routine Prototypes
269 */
270
271rtems_libio_t *rtems_libio_allocate(void);
272
273uint32_t rtems_libio_fcntl_flags( int fcntl_flags );
274
275int rtems_libio_to_fcntl_flags( uint32_t flags );
276
277void rtems_libio_free(
278  rtems_libio_t *iop
279);
280
281/*
282 *  File System Routine Prototypes
283 */
284
285rtems_filesystem_location_info_t *
286rtems_filesystem_eval_path_start(
287  rtems_filesystem_eval_path_context_t *ctx,
288  const char *path,
289  int eval_flags
290);
291
292rtems_filesystem_location_info_t *
293rtems_filesystem_eval_path_start_with_parent(
294  rtems_filesystem_eval_path_context_t *ctx,
295  const char *path,
296  int eval_flags,
297  rtems_filesystem_location_info_t *parentloc,
298  int parent_eval_flags
299);
300
301rtems_filesystem_location_info_t *
302rtems_filesystem_eval_path_start_with_root_and_current(
303  rtems_filesystem_eval_path_context_t *ctx,
304  const char *path,
305  int eval_flags,
306  rtems_filesystem_global_location_t *const *global_root_ptr,
307  rtems_filesystem_global_location_t *const *global_current_ptr
308);
309
310void rtems_filesystem_eval_path_continue(
311  rtems_filesystem_eval_path_context_t *ctx
312);
313
314void rtems_filesystem_eval_path_cleanup(
315  rtems_filesystem_eval_path_context_t *ctx
316);
317
318void rtems_filesystem_eval_path_recursive(
319  rtems_filesystem_eval_path_context_t *ctx,
320  const char *path,
321  size_t pathlen
322);
323
324void rtems_filesystem_eval_path_cleanup_with_parent(
325  rtems_filesystem_eval_path_context_t *ctx,
326  rtems_filesystem_location_info_t *parentloc
327);
328
329/**
330 * @brief Requests a path evaluation restart.
331 *
332 * Sets the start and current location to the new start location.  The caller
333 * must terminate its current evaluation process.  The path evaluation
334 * continues in the next loop iteration within
335 * rtems_filesystem_eval_path_continue().  This avoids recursive invokations.
336 * The function obtains the new start location and clones it to set the new
337 * current location.  The previous start and current locations are released.
338 *
339 * @param[in, out] ctx The path evaluation context.
340 * @param[in, out] newstartloc_ptr Pointer to new start location.
341 */
342void rtems_filesystem_eval_path_restart(
343  rtems_filesystem_eval_path_context_t *ctx,
344  rtems_filesystem_global_location_t **newstartloc_ptr
345);
346
347typedef enum {
348  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE,
349  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE,
350  RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY
351} rtems_filesystem_eval_path_generic_status;
352
353/**
354 * @brief Tests if the current location is a directory.
355 *
356 * @param[in, out] ctx The path evaluation context.
357 * @param[in, out] arg The handler argument.
358 *
359 * @retval true The current location is a directory.
360 * @retval false Otherwise.
361 *
362 * @see rtems_filesystem_eval_path_generic().
363 */
364typedef bool (*rtems_filesystem_eval_path_is_directory)(
365  rtems_filesystem_eval_path_context_t *ctx,
366  void *arg
367);
368
369/**
370 * @brief Evaluates a token.
371 *
372 * @param[in, out] ctx The path evaluation context.
373 * @param[in, out] arg The handler argument.
374 * @param[in] token The token contents.
375 * @param[in] tokenlen The token length in characters.
376 *
377 * @retval status The generic path evaluation status.
378 *
379 * @see rtems_filesystem_eval_path_generic().
380 */
381typedef rtems_filesystem_eval_path_generic_status
382(*rtems_filesystem_eval_path_eval_token)(
383  rtems_filesystem_eval_path_context_t *ctx,
384  void *arg,
385  const char *token,
386  size_t tokenlen
387);
388
389typedef struct {
390  rtems_filesystem_eval_path_is_directory is_directory;
391  rtems_filesystem_eval_path_eval_token eval_token;
392} rtems_filesystem_eval_path_generic_config;
393
394void rtems_filesystem_eval_path_generic(
395  rtems_filesystem_eval_path_context_t *ctx,
396  void *arg,
397  const rtems_filesystem_eval_path_generic_config *config
398);
399
400void rtems_filesystem_initialize(void);
401
402/**
403 * @brief Copies a location.
404 *
405 * A bitwise copy is performed.  The destination location will be added to the
406 * corresponding mount entry.
407 *
408 * @param[out] dst The destination location.
409 * @param[in] src The source location.
410 *
411 * @retval dst The destination location.
412 *
413 * @see rtems_filesystem_location_clone().
414 */
415rtems_filesystem_location_info_t *rtems_filesystem_location_copy(
416  rtems_filesystem_location_info_t *dst,
417  const rtems_filesystem_location_info_t *src
418);
419
420static inline rtems_filesystem_location_info_t *
421rtems_filesystem_location_initialize_to_null(
422  rtems_filesystem_location_info_t *loc
423)
424{
425  return rtems_filesystem_location_copy(
426    loc,
427    &rtems_filesystem_global_location_null.location
428  );
429}
430
431rtems_filesystem_global_location_t *
432rtems_filesystem_location_transform_to_global(
433  rtems_filesystem_location_info_t *loc
434);
435
436/**
437 * @brief Assigns a global file system location.
438 *
439 * @param[in, out] lhs_global_loc_ptr Pointer to the global left hand side file
440 * system location.  The current left hand side location will be released.
441 * @param[in] rhs_global_loc The global right hand side file system location.
442 */
443void rtems_filesystem_global_location_assign(
444  rtems_filesystem_global_location_t **lhs_global_loc_ptr,
445  rtems_filesystem_global_location_t *rhs_global_loc
446);
447
448/**
449 * @brief Obtains a global file system location.
450 *
451 * Deferred releases will be processed in this function.
452 *
453 * This function must be called from normal thread context and may block on a
454 * mutex.  Thread dispatching is disabled to protect some critical sections.
455 *
456 * @param[in] global_loc_ptr Pointer to the global file system location.
457 *
458 * @return A global file system location.  It returns always a valid object.
459 * In case of an error, the global null location will be returned.  Each
460 * operation or handler of the null location returns an error status.  The
461 * errno indicates the error.  The NULL pointer is never returned.
462 *
463 * @see rtems_filesystem_location_transform_to_global(),
464 * rtems_filesystem_global_location_obtain_null(), and
465 * rtems_filesystem_global_location_release().
466 */
467rtems_filesystem_global_location_t *rtems_filesystem_global_location_obtain(
468  rtems_filesystem_global_location_t *const *global_loc_ptr
469);
470
471/**
472 * @brief Releases a global file system location.
473 *
474 * In case the reference count reaches zero, all associated resources will be
475 * released.  This may include the complete unmount of the corresponding file
476 * system instance.
477 *
478 * This function may block on a mutex.  It may be called within critical
479 * sections of the operating system.  In this case the release will be
480 * deferred.  The next obtain call will do the actual release.
481 *
482 * @param[in] global_loc The global file system location.  It must not be NULL.
483 *
484 * @see rtems_filesystem_global_location_obtain().
485 */
486void rtems_filesystem_global_location_release(
487  rtems_filesystem_global_location_t *global_loc
488);
489
490void rtems_filesystem_location_detach(
491  rtems_filesystem_location_info_t *detach
492);
493
494void rtems_filesystem_location_copy_and_detach(
495  rtems_filesystem_location_info_t *copy,
496  rtems_filesystem_location_info_t *detach
497);
498
499static inline rtems_filesystem_global_location_t *
500rtems_filesystem_global_location_obtain_null(void)
501{
502  rtems_filesystem_global_location_t *global_loc = NULL;
503
504  return rtems_filesystem_global_location_obtain( &global_loc );
505}
506
507static inline bool rtems_filesystem_location_is_null(
508  const rtems_filesystem_location_info_t *loc
509)
510{
511  return loc->handlers == &rtems_filesystem_null_handlers;
512}
513
514static inline bool rtems_filesystem_global_location_is_null(
515  const rtems_filesystem_global_location_t *global_loc
516)
517{
518  return rtems_filesystem_location_is_null( &global_loc->location );
519}
520
521static inline void rtems_filesystem_location_error(
522  const rtems_filesystem_location_info_t *loc,
523  int eno
524)
525{
526  if ( !rtems_filesystem_location_is_null( loc ) ) {
527    errno = eno;
528  }
529}
530
531int rtems_filesystem_mknod(
532  const rtems_filesystem_location_info_t *parentloc,
533  const char *name,
534  size_t namelen,
535  mode_t mode,
536  dev_t dev
537);
538
539int rtems_filesystem_chdir( rtems_filesystem_location_info_t *loc );
540
541int rtems_filesystem_chown(
542  const char *path,
543  uid_t owner,
544  gid_t group,
545  int eval_follow_link
546);
547
548static inline bool rtems_filesystem_is_ready_for_unmount(
549  rtems_filesystem_mount_table_entry_t *mt_entry
550)
551{
552  bool ready = !mt_entry->mounted
553    && rtems_chain_has_only_one_node( &mt_entry->location_chain )
554    && mt_entry->mt_fs_root->reference_count == 1;
555
556  if ( ready ) {
557    rtems_chain_initialize_empty( &mt_entry->location_chain );
558  }
559
560  return ready;
561}
562
563static inline void rtems_filesystem_location_add_to_mt_entry(
564  rtems_filesystem_location_info_t *loc
565)
566{
567  rtems_filesystem_mt_entry_declare_lock_context( lock_context );
568
569  rtems_filesystem_mt_entry_lock( lock_context );
570  rtems_chain_append_unprotected(
571    &loc->mt_entry->location_chain,
572    &loc->mt_entry_node
573  );
574  rtems_filesystem_mt_entry_unlock( lock_context );
575}
576
577void rtems_filesystem_location_remove_from_mt_entry(
578  rtems_filesystem_location_info_t *loc
579);
580
581void rtems_filesystem_do_unmount(
582  rtems_filesystem_mount_table_entry_t *mt_entry
583);
584
585static inline bool rtems_filesystem_location_is_root(
586  const rtems_filesystem_location_info_t *loc
587)
588{
589  const rtems_filesystem_mount_table_entry_t *mt_entry = loc->mt_entry;
590
591  return (*mt_entry->ops->are_nodes_equal_h)(
592    loc,
593    &mt_entry->mt_fs_root->location
594  );
595}
596
597static inline const char *rtems_filesystem_eval_path_get_path(
598  rtems_filesystem_eval_path_context_t *ctx
599)
600{
601  return ctx->path;
602}
603
604static inline size_t rtems_filesystem_eval_path_get_pathlen(
605  rtems_filesystem_eval_path_context_t *ctx
606)
607{
608  return ctx->pathlen;
609}
610
611static inline void rtems_filesystem_eval_path_set_path(
612  rtems_filesystem_eval_path_context_t *ctx,
613  const char *path,
614  size_t pathlen
615)
616{
617  ctx->path = path;
618  ctx->pathlen = pathlen;
619}
620
621static inline void rtems_filesystem_eval_path_clear_path(
622  rtems_filesystem_eval_path_context_t *ctx
623)
624{
625  ctx->pathlen = 0;
626}
627
628static inline const char *rtems_filesystem_eval_path_get_token(
629  rtems_filesystem_eval_path_context_t *ctx
630)
631{
632  return ctx->token;
633}
634
635static inline size_t rtems_filesystem_eval_path_get_tokenlen(
636  rtems_filesystem_eval_path_context_t *ctx
637)
638{
639  return ctx->tokenlen;
640}
641
642static inline void rtems_filesystem_eval_path_set_token(
643  rtems_filesystem_eval_path_context_t *ctx,
644  const char *token,
645  size_t tokenlen
646)
647{
648  ctx->token = token;
649  ctx->tokenlen = tokenlen;
650}
651
652static inline void rtems_filesystem_eval_path_clear_token(
653  rtems_filesystem_eval_path_context_t *ctx
654)
655{
656  ctx->tokenlen = 0;
657}
658
659static inline void rtems_filesystem_eval_path_put_back_token(
660  rtems_filesystem_eval_path_context_t *ctx
661)
662{
663  size_t tokenlen = ctx->tokenlen;
664
665  ctx->path -= tokenlen;
666  ctx->pathlen += tokenlen;
667  ctx->tokenlen = 0;
668}
669
670void rtems_filesystem_eval_path_eat_delimiter(
671  rtems_filesystem_eval_path_context_t *ctx
672);
673
674void rtems_filesystem_eval_path_next_token(
675  rtems_filesystem_eval_path_context_t *ctx
676);
677
678static inline void rtems_filesystem_eval_path_get_next_token(
679  rtems_filesystem_eval_path_context_t *ctx,
680  const char **token,
681  size_t *tokenlen
682)
683{
684  rtems_filesystem_eval_path_next_token(ctx);
685  *token = ctx->token;
686  *tokenlen = ctx->tokenlen;
687}
688
689static inline rtems_filesystem_location_info_t *
690rtems_filesystem_eval_path_get_currentloc(
691  rtems_filesystem_eval_path_context_t *ctx
692)
693{
694  return &ctx->currentloc;
695}
696
697static inline bool rtems_filesystem_eval_path_has_path(
698  const rtems_filesystem_eval_path_context_t *ctx
699)
700{
701  return ctx->pathlen > 0;
702}
703
704static inline bool rtems_filesystem_eval_path_has_token(
705  const rtems_filesystem_eval_path_context_t *ctx
706)
707{
708  return ctx->tokenlen > 0;
709}
710
711static inline int rtems_filesystem_eval_path_get_flags(
712  const rtems_filesystem_eval_path_context_t *ctx
713)
714{
715  return ctx->flags;
716}
717
718static inline void rtems_filesystem_eval_path_set_flags(
719  rtems_filesystem_eval_path_context_t *ctx,
720  int flags
721)
722{
723  ctx->flags = flags;
724}
725
726static inline void rtems_filesystem_eval_path_clear_and_set_flags(
727  rtems_filesystem_eval_path_context_t *ctx,
728  int clear,
729  int set
730)
731{
732  int flags = ctx->flags;
733
734  flags &= ~clear;
735  flags |= set;
736
737  ctx->flags = flags;
738}
739
740static inline void rtems_filesystem_eval_path_extract_currentloc(
741  rtems_filesystem_eval_path_context_t *ctx,
742  rtems_filesystem_location_info_t *get
743)
744{
745  rtems_filesystem_location_copy_and_detach(
746    get,
747    &ctx->currentloc
748  );
749}
750
751void rtems_filesystem_eval_path_error(
752  rtems_filesystem_eval_path_context_t *ctx,
753  int eno
754);
755
756/**
757 * @brief Checks that the locations exist in the same file system instance.
758 *
759 * @retval 0 The locations exist and are in the same file system instance.
760 * @retval -1 An error occured.  The @c errno indicates the error.
761 */
762int rtems_filesystem_location_exists_in_same_fs_instance_as(
763  const rtems_filesystem_location_info_t *a,
764  const rtems_filesystem_location_info_t *b
765);
766
767bool rtems_filesystem_check_access(
768  int eval_flags,
769  mode_t node_mode,
770  uid_t node_uid,
771  gid_t node_gid
772);
773
774bool rtems_filesystem_eval_path_check_access(
775  rtems_filesystem_eval_path_context_t *ctx,
776  int eval_flags,
777  mode_t node_mode,
778  uid_t node_uid,
779  gid_t node_gid
780);
781
782static inline bool rtems_filesystem_is_delimiter(char c)
783{
784  return c == '/' || c == '\\';
785}
786
787static inline bool rtems_filesystem_is_current_directory(
788  const char *token,
789  size_t tokenlen
790)
791{
792  return tokenlen == 1 && token [0] == '.';
793}
794
795static inline bool rtems_filesystem_is_parent_directory(
796  const char *token,
797  size_t tokenlen
798)
799{
800  return tokenlen == 2 && token [0] == '.' && token [1] == '.';
801}
802
803#ifdef __cplusplus
804}
805#endif
806
807#endif
808/* end of include file */
Note: See TracBrowser for help on using the repository browser.