root/ext/standard/dir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_set_default_dir
  2. PHP_RINIT_FUNCTION
  3. PHP_MINIT_FUNCTION
  4. _php_do_opendir
  5. PHP_FUNCTION
  6. PHP_FUNCTION
  7. PHP_FUNCTION
  8. PHP_FUNCTION
  9. PHP_FUNCTION
  10. PHP_FUNCTION
  11. PHP_FUNCTION
  12. PHP_NAMED_FUNCTION
  13. PHP_FUNCTION
  14. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Author: Thies C. Arntzen <thies@thieso.net>                          |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 /* {{{ includes/startup/misc */
  22 
  23 #include "php.h"
  24 #include "fopen_wrappers.h"
  25 #include "file.h"
  26 #include "php_dir.h"
  27 #include "php_string.h"
  28 #include "php_scandir.h"
  29 #include "basic_functions.h"
  30 
  31 #ifdef HAVE_DIRENT_H
  32 #include <dirent.h>
  33 #endif
  34 
  35 #if HAVE_UNISTD_H
  36 #include <unistd.h>
  37 #endif
  38 
  39 #include <errno.h>
  40 
  41 #ifdef PHP_WIN32
  42 #include "win32/readdir.h"
  43 #endif
  44 
  45 
  46 #ifdef HAVE_GLOB
  47 #ifndef PHP_WIN32
  48 #include <glob.h>
  49 #else
  50 #include "win32/glob.h"
  51 #endif
  52 #endif
  53 
  54 typedef struct {
  55         zend_resource *default_dir;
  56 } php_dir_globals;
  57 
  58 #ifdef ZTS
  59 #define DIRG(v) ZEND_TSRMG(dir_globals_id, php_dir_globals *, v)
  60 int dir_globals_id;
  61 #else
  62 #define DIRG(v) (dir_globals.v)
  63 php_dir_globals dir_globals;
  64 #endif
  65 
  66 #if 0
  67 typedef struct {
  68         int id;
  69         DIR *dir;
  70 } php_dir;
  71 
  72 static int le_dirp;
  73 #endif
  74 
  75 static zend_class_entry *dir_class_entry_ptr;
  76 
  77 #define FETCH_DIRP() \
  78         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &id) == FAILURE) { \
  79                 return; \
  80         } \
  81         if (ZEND_NUM_ARGS() == 0) { \
  82                 myself = getThis(); \
  83                 if (myself) { \
  84                         if ((tmp = zend_hash_str_find(Z_OBJPROP_P(myself), "handle", sizeof("handle")-1)) == NULL) { \
  85                                 php_error_docref(NULL, E_WARNING, "Unable to find my handle property"); \
  86                                 RETURN_FALSE; \
  87                         } \
  88                         if ((dirp = (php_stream *)zend_fetch_resource_ex(tmp, "Directory", php_file_le_stream())) == NULL) { \
  89                                 RETURN_FALSE; \
  90                         } \
  91                 } else { \
  92                         if (!DIRG(default_dir) || \
  93                                 (dirp = (php_stream *)zend_fetch_resource(DIRG(default_dir), "Directory", php_file_le_stream())) == NULL) { \
  94                                 RETURN_FALSE; \
  95                         } \
  96                 } \
  97         } else { \
  98                 if ((dirp = (php_stream *)zend_fetch_resource(Z_RES_P(id), "Directory", php_file_le_stream())) == NULL) { \
  99                         RETURN_FALSE; \
 100                 } \
 101         }
 102 
 103 /* {{{ arginfo */
 104 ZEND_BEGIN_ARG_INFO_EX(arginfo_dir, 0, 0, 0)
 105         ZEND_ARG_INFO(0, dir_handle)
 106 ZEND_END_ARG_INFO()
 107 /* }}} */
 108 
 109 static const zend_function_entry php_dir_class_functions[] = {
 110         PHP_FALIAS(close,       closedir,               arginfo_dir)
 111         PHP_FALIAS(rewind,      rewinddir,              arginfo_dir)
 112         PHP_NAMED_FE(read,  php_if_readdir, arginfo_dir)
 113         {NULL, NULL, NULL}
 114 };
 115 
 116 
 117 static void php_set_default_dir(zend_resource *res)
 118 {
 119         if (DIRG(default_dir)) {
 120                 zend_list_delete(DIRG(default_dir));
 121         }
 122 
 123         if (res) {
 124                 GC_REFCOUNT(res)++;
 125         }
 126 
 127         DIRG(default_dir) = res;
 128 }
 129 
 130 PHP_RINIT_FUNCTION(dir)
 131 {
 132         DIRG(default_dir) = NULL;
 133         return SUCCESS;
 134 }
 135 
 136 PHP_MINIT_FUNCTION(dir)
 137 {
 138         static char dirsep_str[2], pathsep_str[2];
 139         zend_class_entry dir_class_entry;
 140 
 141         INIT_CLASS_ENTRY(dir_class_entry, "Directory", php_dir_class_functions);
 142         dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry);
 143 
 144 #ifdef ZTS
 145         ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
 146 #endif
 147 
 148         dirsep_str[0] = DEFAULT_SLASH;
 149         dirsep_str[1] = '\0';
 150         REGISTER_STRING_CONSTANT("DIRECTORY_SEPARATOR", dirsep_str, CONST_CS|CONST_PERSISTENT);
 151 
 152         pathsep_str[0] = ZEND_PATHS_SEPARATOR;
 153         pathsep_str[1] = '\0';
 154         REGISTER_STRING_CONSTANT("PATH_SEPARATOR", pathsep_str, CONST_CS|CONST_PERSISTENT);
 155 
 156         REGISTER_LONG_CONSTANT("SCANDIR_SORT_ASCENDING",  PHP_SCANDIR_SORT_ASCENDING,  CONST_CS | CONST_PERSISTENT);
 157         REGISTER_LONG_CONSTANT("SCANDIR_SORT_DESCENDING", PHP_SCANDIR_SORT_DESCENDING, CONST_CS | CONST_PERSISTENT);
 158         REGISTER_LONG_CONSTANT("SCANDIR_SORT_NONE",       PHP_SCANDIR_SORT_NONE,       CONST_CS | CONST_PERSISTENT);
 159 
 160 #ifdef HAVE_GLOB
 161 
 162 #ifdef GLOB_BRACE
 163         REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE, CONST_CS | CONST_PERSISTENT);
 164 #else
 165 # define GLOB_BRACE 0
 166 #endif
 167 
 168 #ifdef GLOB_MARK
 169         REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK, CONST_CS | CONST_PERSISTENT);
 170 #else
 171 # define GLOB_MARK 0
 172 #endif
 173 
 174 #ifdef GLOB_NOSORT
 175         REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT, CONST_CS | CONST_PERSISTENT);
 176 #else
 177 # define GLOB_NOSORT 0
 178 #endif
 179 
 180 #ifdef GLOB_NOCHECK
 181         REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK, CONST_CS | CONST_PERSISTENT);
 182 #else
 183 # define GLOB_NOCHECK 0
 184 #endif
 185 
 186 #ifdef GLOB_NOESCAPE
 187         REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE, CONST_CS | CONST_PERSISTENT);
 188 #else
 189 # define GLOB_NOESCAPE 0
 190 #endif
 191 
 192 #ifdef GLOB_ERR
 193         REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR, CONST_CS | CONST_PERSISTENT);
 194 #else
 195 # define GLOB_ERR 0
 196 #endif
 197 
 198 #ifndef GLOB_ONLYDIR
 199 # define GLOB_ONLYDIR (1<<30)
 200 # define GLOB_EMULATE_ONLYDIR
 201 # define GLOB_FLAGMASK (~GLOB_ONLYDIR)
 202 #else
 203 # define GLOB_FLAGMASK (~0)
 204 #endif
 205 
 206 /* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */
 207 #define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR)
 208 
 209         REGISTER_LONG_CONSTANT("GLOB_ONLYDIR", GLOB_ONLYDIR, CONST_CS | CONST_PERSISTENT);
 210         REGISTER_LONG_CONSTANT("GLOB_AVAILABLE_FLAGS", GLOB_AVAILABLE_FLAGS, CONST_CS | CONST_PERSISTENT);
 211 
 212 #endif /* HAVE_GLOB */
 213 
 214         return SUCCESS;
 215 }
 216 /* }}} */
 217 
 218 /* {{{ internal functions */
 219 static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
 220 {
 221         char *dirname;
 222         size_t dir_len;
 223         zval *zcontext = NULL;
 224         php_stream_context *context = NULL;
 225         php_stream *dirp;
 226 
 227         if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|r", &dirname, &dir_len, &zcontext) == FAILURE) {
 228                 RETURN_NULL();
 229         }
 230 
 231         context = php_stream_context_from_zval(zcontext, 0);
 232 
 233         dirp = php_stream_opendir(dirname, REPORT_ERRORS, context);
 234 
 235         if (dirp == NULL) {
 236                 RETURN_FALSE;
 237         }
 238 
 239         dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
 240 
 241         php_set_default_dir(dirp->res);
 242 
 243         if (createobject) {
 244                 object_init_ex(return_value, dir_class_entry_ptr);
 245                 add_property_stringl(return_value, "path", dirname, dir_len);
 246                 add_property_resource(return_value, "handle", dirp->res);
 247                 php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
 248         } else {
 249                 php_stream_to_zval(dirp, return_value);
 250         }
 251 }
 252 /* }}} */
 253 
 254 /* {{{ proto mixed opendir(string path[, resource context])
 255    Open a directory and return a dir_handle */
 256 PHP_FUNCTION(opendir)
 257 {
 258         _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
 259 }
 260 /* }}} */
 261 
 262 /* {{{ proto object dir(string directory[, resource context])
 263    Directory class with properties, handle and class and methods read, rewind and close */
 264 PHP_FUNCTION(getdir)
 265 {
 266         _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 267 }
 268 /* }}} */
 269 
 270 /* {{{ proto void closedir([resource dir_handle])
 271    Close directory connection identified by the dir_handle */
 272 PHP_FUNCTION(closedir)
 273 {
 274         zval *id = NULL, *tmp, *myself;
 275         php_stream *dirp;
 276         zend_resource *res;
 277 
 278         FETCH_DIRP();
 279 
 280         if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
 281                 php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
 282                 RETURN_FALSE;
 283         }
 284 
 285         res = dirp->res;
 286         zend_list_close(dirp->res);
 287 
 288         if (res == DIRG(default_dir)) {
 289                 php_set_default_dir(NULL);
 290         }
 291 }
 292 /* }}} */
 293 
 294 #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
 295 /* {{{ proto bool chroot(string directory)
 296    Change root directory */
 297 PHP_FUNCTION(chroot)
 298 {
 299         char *str;
 300         int ret;
 301         size_t str_len;
 302 
 303         if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &str, &str_len) == FAILURE) {
 304                 RETURN_FALSE;
 305         }
 306 
 307         ret = chroot(str);
 308         if (ret != 0) {
 309                 php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
 310                 RETURN_FALSE;
 311         }
 312 
 313         php_clear_stat_cache(1, NULL, 0);
 314 
 315         ret = chdir("/");
 316 
 317         if (ret != 0) {
 318                 php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
 319                 RETURN_FALSE;
 320         }
 321 
 322         RETURN_TRUE;
 323 }
 324 /* }}} */
 325 #endif
 326 
 327 /* {{{ proto bool chdir(string directory)
 328    Change the current directory */
 329 PHP_FUNCTION(chdir)
 330 {
 331         char *str;
 332         int ret;
 333         size_t str_len;
 334 
 335         if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &str, &str_len) == FAILURE) {
 336                 RETURN_FALSE;
 337         }
 338 
 339         if (php_check_open_basedir(str)) {
 340                 RETURN_FALSE;
 341         }
 342         ret = VCWD_CHDIR(str);
 343 
 344         if (ret != 0) {
 345                 php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
 346                 RETURN_FALSE;
 347         }
 348 
 349         if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
 350                 efree(BG(CurrentStatFile));
 351                 BG(CurrentStatFile) = NULL;
 352         }
 353         if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
 354                 efree(BG(CurrentLStatFile));
 355                 BG(CurrentLStatFile) = NULL;
 356         }
 357 
 358         RETURN_TRUE;
 359 }
 360 /* }}} */
 361 
 362 /* {{{ proto mixed getcwd(void)
 363    Gets the current directory */
 364 PHP_FUNCTION(getcwd)
 365 {
 366         char path[MAXPATHLEN];
 367         char *ret=NULL;
 368 
 369         if (zend_parse_parameters_none() == FAILURE) {
 370                 return;
 371         }
 372 
 373 #if HAVE_GETCWD
 374         ret = VCWD_GETCWD(path, MAXPATHLEN);
 375 #elif HAVE_GETWD
 376         ret = VCWD_GETWD(path);
 377 #endif
 378 
 379         if (ret) {
 380                 RETURN_STRING(path);
 381         } else {
 382                 RETURN_FALSE;
 383         }
 384 }
 385 /* }}} */
 386 
 387 /* {{{ proto void rewinddir([resource dir_handle])
 388    Rewind dir_handle back to the start */
 389 PHP_FUNCTION(rewinddir)
 390 {
 391         zval *id = NULL, *tmp, *myself;
 392         php_stream *dirp;
 393 
 394         FETCH_DIRP();
 395 
 396         if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
 397                 php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
 398                 RETURN_FALSE;
 399         }
 400 
 401         php_stream_rewinddir(dirp);
 402 }
 403 /* }}} */
 404 
 405 /* {{{ proto string readdir([resource dir_handle])
 406    Read directory entry from dir_handle */
 407 PHP_NAMED_FUNCTION(php_if_readdir)
 408 {
 409         zval *id = NULL, *tmp, *myself;
 410         php_stream *dirp;
 411         php_stream_dirent entry;
 412 
 413         FETCH_DIRP();
 414 
 415         if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
 416                 php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
 417                 RETURN_FALSE;
 418         }
 419 
 420         if (php_stream_readdir(dirp, &entry)) {
 421                 RETURN_STRINGL(entry.d_name, strlen(entry.d_name));
 422         }
 423         RETURN_FALSE;
 424 }
 425 /* }}} */
 426 
 427 #ifdef HAVE_GLOB
 428 /* {{{ proto array glob(string pattern [, int flags])
 429    Find pathnames matching a pattern */
 430 PHP_FUNCTION(glob)
 431 {
 432         int cwd_skip = 0;
 433 #ifdef ZTS
 434         char cwd[MAXPATHLEN];
 435         char work_pattern[MAXPATHLEN];
 436         char *result;
 437 #endif
 438         char *pattern = NULL;
 439         size_t pattern_len;
 440         zend_long flags = 0;
 441         glob_t globbuf;
 442         int n;
 443         int ret;
 444         zend_bool basedir_limit = 0;
 445 
 446         if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &pattern, &pattern_len, &flags) == FAILURE) {
 447                 return;
 448         }
 449 
 450         if (pattern_len >= MAXPATHLEN) {
 451                 php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
 452                 RETURN_FALSE;
 453         }
 454 
 455         if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
 456                 php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
 457                 RETURN_FALSE;
 458         }
 459 
 460 #ifdef ZTS
 461         if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
 462                 result = VCWD_GETCWD(cwd, MAXPATHLEN);
 463                 if (!result) {
 464                         cwd[0] = '\0';
 465                 }
 466 #ifdef PHP_WIN32
 467                 if (IS_SLASH(*pattern)) {
 468                         cwd[2] = '\0';
 469                 }
 470 #endif
 471                 cwd_skip = (int)strlen(cwd)+1;
 472 
 473                 snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
 474                 pattern = work_pattern;
 475         }
 476 #endif
 477 
 478 
 479         memset(&globbuf, 0, sizeof(glob_t));
 480         globbuf.gl_offs = 0;
 481         if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
 482 #ifdef GLOB_NOMATCH
 483                 if (GLOB_NOMATCH == ret) {
 484                         /* Some glob implementation simply return no data if no matches
 485                            were found, others return the GLOB_NOMATCH error code.
 486                            We don't want to treat GLOB_NOMATCH as an error condition
 487                            so that PHP glob() behaves the same on both types of
 488                            implementations and so that 'foreach (glob() as ...'
 489                            can be used for simple glob() calls without further error
 490                            checking.
 491                         */
 492                         goto no_results;
 493                 }
 494 #endif
 495                 RETURN_FALSE;
 496         }
 497 
 498         /* now catch the FreeBSD style of "no matches" */
 499         if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
 500 no_results:
 501 #ifndef PHP_WIN32
 502                 /* Paths containing '*', '?' and some other chars are
 503                 illegal on Windows but legit on other platforms. For
 504                 this reason the direct basedir check against the glob
 505                 query is senseless on windows. For instance while *.txt
 506                 is a pretty valid filename on EXT3, it's invalid on NTFS. */
 507                 if (PG(open_basedir) && *PG(open_basedir)) {
 508                         if (php_check_open_basedir_ex(pattern, 0)) {
 509                                 RETURN_FALSE;
 510                         }
 511                 }
 512 #endif
 513                 array_init(return_value);
 514                 return;
 515         }
 516 
 517         array_init(return_value);
 518         for (n = 0; n < globbuf.gl_pathc; n++) {
 519                 if (PG(open_basedir) && *PG(open_basedir)) {
 520                         if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0)) {
 521                                 basedir_limit = 1;
 522                                 continue;
 523                         }
 524                 }
 525                 /* we need to do this every time since GLOB_ONLYDIR does not guarantee that
 526                  * all directories will be filtered. GNU libc documentation states the
 527                  * following:
 528                  * If the information about the type of the file is easily available
 529                  * non-directories will be rejected but no extra work will be done to
 530                  * determine the information for each file. I.e., the caller must still be
 531                  * able to filter directories out.
 532                  */
 533                 if (flags & GLOB_ONLYDIR) {
 534                         zend_stat_t s;
 535 
 536                         if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
 537                                 continue;
 538                         }
 539 
 540                         if (S_IFDIR != (s.st_mode & S_IFMT)) {
 541                                 continue;
 542                         }
 543                 }
 544                 add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip);
 545         }
 546 
 547         globfree(&globbuf);
 548 
 549         if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
 550                 zval_dtor(return_value);
 551                 RETURN_FALSE;
 552         }
 553 }
 554 /* }}} */
 555 #endif
 556 
 557 /* {{{ proto array scandir(string dir [, int sorting_order [, resource context]])
 558    List files & directories inside the specified path */
 559 PHP_FUNCTION(scandir)
 560 {
 561         char *dirn;
 562         size_t dirn_len;
 563         zend_long flags = 0;
 564         zend_string **namelist;
 565         int n, i;
 566         zval *zcontext = NULL;
 567         php_stream_context *context = NULL;
 568 
 569         if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lr", &dirn, &dirn_len, &flags, &zcontext) == FAILURE) {
 570                 return;
 571         }
 572 
 573         if (dirn_len < 1) {
 574                 php_error_docref(NULL, E_WARNING, "Directory name cannot be empty");
 575                 RETURN_FALSE;
 576         }
 577 
 578         if (zcontext) {
 579                 context = php_stream_context_from_zval(zcontext, 0);
 580         }
 581 
 582         if (flags == PHP_SCANDIR_SORT_ASCENDING) {
 583                 n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
 584         } else if (flags == PHP_SCANDIR_SORT_NONE) {
 585                 n = php_stream_scandir(dirn, &namelist, context, NULL);
 586         } else {
 587                 n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
 588         }
 589         if (n < 0) {
 590                 php_error_docref(NULL, E_WARNING, "(errno %d): %s", errno, strerror(errno));
 591                 RETURN_FALSE;
 592         }
 593 
 594         array_init(return_value);
 595 
 596         for (i = 0; i < n; i++) {
 597                 add_next_index_str(return_value, namelist[i]);
 598         }
 599 
 600         if (n) {
 601                 efree(namelist);
 602         }
 603 }
 604 /* }}} */
 605 
 606 /*
 607  * Local variables:
 608  * tab-width: 4
 609  * c-basic-offset: 4
 610  * End:
 611  * vim600: sw=4 ts=4 fdm=marker
 612  * vim<600: sw=4 ts=4
 613  */

/* [<][>][^][v][top][bottom][index][help] */