📄 getcwd.c
字号:
/* *---------------------------------------------------------------------- * T-Kernel / Standard Extension * * Copyright (C) 2006 by Ken Sakamura. All rights reserved. * T-Kernel / Standard Extension is distributed * under the T-License for T-Kernel / Standard Extension. *---------------------------------------------------------------------- * * Version: 1.00.00 * Released by T-Engine Forum(http://www.t-engine.org) at 2006/8/11. * *---------------------------------------------------------------------- *//* * @(#)getcwd.c (seio) * */#include <basic.h>#include <tk/tkernel.h>#include <seio/unistd.h>#include <seio/sys/types.h>#include <seio/sys/stat.h>#include <seio/dirent.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include "sfimport.h"IMPORT DIR* opendir( const char *path );IMPORT struct dirent* readdir( DIR *dp );IMPORT int closedir( DIR *dp );struct pathbuf { B *buf; /* Buffer for storing path name */ size_t bufsz; /* Size of "buf" (0 when a fixed length is specified) */ B *cp; /* Current position */ B *ep; /* Termination position */};#define EXBUFSZ 256U /* Extended unit (byte count) of the buffer for storing path name *//* ------------------------------------------------------------------------ *//* * Path name currently being stored */#define cupdir(pb) ( (pb)->buf )/* * The save and restoring of the current path name */#define saveupdir(pb) ( (pb)->cp - (pb)->buf )#define restoreupdir(pb, n) ( *((pb)->cp = (pb)->buf + (n)) = '\0' )#define SIZE_2_CHK 2U /* size check *//* * Initialize the buffer for creating the path name of parent dirtectory */static void initupdir( struct pathbuf *pb ){ pb->buf = NULL; pb->bufsz = 0; pb->cp = NULL; pb->ep = NULL;}/* * Create the path name to trace a parent directory * Add "name" to the behind the path name. * Return the pointer referring to the path name character-string to return value. * Return "NULL" when error occurs. */static char* updir( struct pathbuf *pb, const char *name, size_t len ){ size_t sz; B *p; sz = (size_t)(pb->ep - pb->cp); if ( (len + 1U) > sz ) { /* +1 is the length for '\0' at the termination */ /* Buffer expansion */ sz = (((len + 1U) - sz + (EXBUFSZ-1U)) / EXBUFSZ) * EXBUFSZ; p = realloc(pb->buf, pb->bufsz + sz); if ( p == NULL ) { return NULL; } pb->bufsz += sz; pb->cp = p + (pb->cp - pb->buf); pb->ep = p + pb->bufsz; pb->buf = p; } /* Connect "name " with backward */ memcpy(pb->cp, name, len); pb->cp += len; *pb->cp = '\0'; return pb->buf;}/* ------------------------------------------------------------------------ *//* * Prepare the buffer for creating the path name of current directory * Fix the size of buffer when "buf != NULL". * Use "buf" to buffer and set "size " to the size. * Allocate the buffer with "malloc()" when "buf == NULL". * Ignore "size". * Return the pointer referring to path name-string to return value. * Return "NULL" when error occurs. */static char* initcwdir( struct pathbuf *pb, char *buf, size_t size ){ B *p; if ( buf == NULL ) { /* Allocate the buffer with "malloc()". */ p = malloc(EXBUFSZ); if ( p == NULL ) { return NULL; } pb->buf = p; pb->bufsz = EXBUFSZ; pb->cp = p + EXBUFSZ; pb->ep = p; } else { /* Fix the buffer */ if ( size < SIZE_2_CHK ) { errno = ( size == 0U )? EINVAL: ERANGE; return NULL; } pb->buf = buf; pb->bufsz = 0; pb->cp = buf + size; pb->ep = buf; } *--pb->cp = '\0'; return pb->cp;}/* * Add "name" to the path name of current directory * Insert '/' before the path name and add "name" * Length of "len" is that of "name" (excluding '\0'). * Return the pointer referring to path name-string to the return value. * Return "NULL" when error occurs. */static char* cwdir( struct pathbuf *pb, const char *name, size_t len ){ size_t sz, n; B *p; /* Constantly allocate the space for one character so that the '/' at the top of path name can * be added */ sz = (size_t)(pb->cp - pb->ep); n = 1; /* Space for the '/' at the top */ if ( *pb->cp != '\0' ) { n += 1U; /* Space for the delimiter '/' (unecessary for termination) */ } if ( (len + n) > sz ) { if ( pb->bufsz == 0U ) { /* Error occurs when it is the buffer fixed length */ errno = ERANGE; return NULL; } /* Buffer expansion */ sz = (((len + n) - sz + (EXBUFSZ-1U)) / EXBUFSZ) * EXBUFSZ; p = realloc(pb->buf, pb->bufsz + sz); if ( p == NULL ) { return NULL; } /* Move the content of buffer to backward, and make room for the front */ memmove(p + sz, p, (size_t)pb->bufsz); pb->cp = p + sz + (pb->cp - pb->buf); pb->ep = p; pb->buf = p; pb->bufsz += sz; } /* Delimit with '/' , connect "name" with front */ if ( *pb->cp != '\0' ) { *--pb->cp = '/'; /* '/' is unnecessary for the termination of path name */ } pb->cp -= len; memcpy(pb->cp, name, len); return pb->cp;}/* * Fix the path name of current directory * Attach '/' with the top of path name, and make an adjustment so that the path name can * be stored from the top of buffer. * Return the pointer referring to path name-string to return value. */static char* fixcwdir( struct pathbuf *pb ){ /* The size of buffer is being adjusted so that the '/' at the top can be certainly inserted */ *--pb->cp = '/'; if ( pb->cp != pb->buf ) { char *sp = pb->cp; char *dp = pb->buf; while ( (*(dp++) = *(sp++)) != '\0' ) { ; } } return pb->buf;}/* ------------------------------------------------------------------------ *//* * Get the absolute path name of current directory * In the case of "buf != NULL", "size" is the size of the array indicated by "buf", and * error occurs when the path name exceeds the "size" byte (including the termination of * character-string '\0').In the case of "buf == NULL", the "size" is ignored, and the * buffer of necessary size for storing path name is obtained by "malloc()" and is set * as "buf".The absolute path name of current directory is stored in "buf",and the "buf" * is retuned to return value. * Return "NULL" to return value when error occurs. */EXPORT char* getcwd( char *buf, size_t size ){ struct pathbuf upd, cwd; struct stat sb; dev_t dev, root_dev; ino_t ino, root_ino; DIR *dir = NULL; struct dirent *de; W save_errno; W n; W same_dev; save_errno = errno; errno = 0; /* Prepare the buffer for storing the path name of current directory */ if ( initcwdir(&cwd, buf, size) == NULL ) { return NULL; } /* Prepare the buffer for storing the path name to trace parent directory */ initupdir(&upd); if ( updir(&upd, ".", 1) == NULL ) { goto err_ret; } /* Root directory */ if ( stat("/", &sb) < 0 ) { goto err_ret; } root_dev = sb.st_dev; root_ino = sb.st_ino; /* Search the path name from current directory toward root directory */ for ( ;; ) { if ( stat(cupdir(&upd), &sb) < 0 ) { goto err_ret; } dev = sb.st_dev; ino = sb.st_ino; /* Complete the search when the pat matches the root directory */ if (( ino == root_ino )&&( dev == root_dev )) { break; } /* Go to next level of directory */ if ( updir(&upd, "/..", 3) == NULL ) { goto err_ret; } /* Search in the parent directory */ dir = opendir(cupdir(&upd)); if ( dir == NULL ) { goto err_ret; } if ( fstat(dirfd(dir), &sb) < 0 ) { goto err_ret; } same_dev = ( sb.st_dev == dev ); for ( ;; ) { de = readdir(dir); if ( de == NULL ) { goto not_exist; } /* OK if it matches "ino" when the identical file system is used */ if ( same_dev != 0 ) { if ( de->d_fileno == ino ) { break; } continue; } /* Exclude "." and ".." */ if (( de->d_name[0] == '.' )&&( (de->d_name[1] == '\0' ) ||((de->d_name[1] == '.' )&&( de->d_name[2] == '\0'))) ) { continue; } /* Also check the consistensy with the file system when different file system is used */ n = saveupdir(&upd); if (( updir(&upd, "/", 1) == NULL ) ||( updir(&upd, de->d_name, de->d_namlen) == NULL )) { goto err_ret; } if ( lstat(cupdir(&upd), &sb) < 0 ) { goto err_ret; } restoreupdir(&upd, n); if (( sb.st_dev == dev )&&( sb.st_ino == ino )) { break; } } /* Connect the found directory name */ if ( cwdir(&cwd, de->d_name, de->d_namlen) == NULL ) { goto err_ret; } (void)closedir(dir); dir = NULL; } free(upd.buf); errno = save_errno; return fixcwdir(&cwd);not_exist: if ( errno == 0 ) { errno = ENOENT; }err_ret: if ( dir != NULL ) { (void)closedir(dir); } if ( cwd.bufsz > 0U ) { free(cwd.buf); } if ( upd.buf != NULL ) { free(upd.buf); } return NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -