153 lines
3.5 KiB
C
153 lines
3.5 KiB
C
|
/* pwd.c
|
|||
|
|
|||
|
Prints the absolute name of the present working directory. */
|
|||
|
|
|||
|
#include <syscall.h>
|
|||
|
#include <stdbool.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
static bool getcwd (char *cwd, size_t cwd_size);
|
|||
|
|
|||
|
int
|
|||
|
main (void)
|
|||
|
{
|
|||
|
char cwd[128];
|
|||
|
if (getcwd (cwd, sizeof cwd))
|
|||
|
{
|
|||
|
printf ("%s\n", cwd);
|
|||
|
return EXIT_SUCCESS;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf ("error\n");
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Stores the inode number for FILE_NAME in *INUM.
|
|||
|
Returns true if successful, false if the file could not be
|
|||
|
opened. */
|
|||
|
static bool
|
|||
|
get_inumber (const char *file_name, int *inum)
|
|||
|
{
|
|||
|
int fd = open (file_name);
|
|||
|
if (fd >= 0)
|
|||
|
{
|
|||
|
*inum = inumber (fd);
|
|||
|
close (fd);
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Prepends PREFIX to the characters stored in the final *DST_LEN
|
|||
|
bytes of the DST_SIZE-byte buffer that starts at DST.
|
|||
|
Returns true if successful, false if adding that many
|
|||
|
characters, plus a null terminator, would overflow the buffer.
|
|||
|
(No null terminator is actually added or depended upon, but
|
|||
|
its space is accounted for.) */
|
|||
|
static bool
|
|||
|
prepend (const char *prefix,
|
|||
|
char *dst, size_t *dst_len, size_t dst_size)
|
|||
|
{
|
|||
|
size_t prefix_len = strlen (prefix);
|
|||
|
if (prefix_len + *dst_len + 1 <= dst_size)
|
|||
|
{
|
|||
|
*dst_len += prefix_len;
|
|||
|
memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Stores the current working directory, as a null-terminated
|
|||
|
string, in the CWD_SIZE bytes in CWD.
|
|||
|
Returns true if successful, false on error. Errors include
|
|||
|
system errors, directory trees deeper than MAX_LEVEL levels,
|
|||
|
and insufficient space in CWD. */
|
|||
|
static bool
|
|||
|
getcwd (char *cwd, size_t cwd_size)
|
|||
|
{
|
|||
|
size_t cwd_len = 0;
|
|||
|
|
|||
|
#define MAX_LEVEL 20
|
|||
|
char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
|
|||
|
char *namep;
|
|||
|
|
|||
|
int child_inum;
|
|||
|
|
|||
|
/* Make sure there's enough space for at least "/". */
|
|||
|
if (cwd_size < 2)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Get inumber for current directory. */
|
|||
|
if (!get_inumber (".", &child_inum))
|
|||
|
return false;
|
|||
|
|
|||
|
namep = name;
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
int parent_inum, parent_fd;
|
|||
|
|
|||
|
/* Compose "../../../..", etc., in NAME. */
|
|||
|
if ((namep - name) > MAX_LEVEL * 3)
|
|||
|
return false;
|
|||
|
*namep++ = '.';
|
|||
|
*namep++ = '.';
|
|||
|
*namep = '\0';
|
|||
|
|
|||
|
/* Open directory. */
|
|||
|
parent_fd = open (name);
|
|||
|
if (parent_fd < 0)
|
|||
|
return false;
|
|||
|
*namep++ = '/';
|
|||
|
|
|||
|
/* If parent and child have the same inumber,
|
|||
|
then we've arrived at the root. */
|
|||
|
parent_inum = inumber (parent_fd);
|
|||
|
if (parent_inum == child_inum)
|
|||
|
break;
|
|||
|
|
|||
|
/* Find name of file in parent directory with the child's
|
|||
|
inumber. */
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
int test_inum;
|
|||
|
if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum))
|
|||
|
{
|
|||
|
close (parent_fd);
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (test_inum == child_inum)
|
|||
|
break;
|
|||
|
}
|
|||
|
close (parent_fd);
|
|||
|
|
|||
|
/* Prepend "/name" to CWD. */
|
|||
|
if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
|
|||
|
return false;
|
|||
|
|
|||
|
/* Move up. */
|
|||
|
child_inum = parent_inum;
|
|||
|
}
|
|||
|
|
|||
|
/* Finalize CWD. */
|
|||
|
if (cwd_len > 0)
|
|||
|
{
|
|||
|
/* Move the string to the beginning of CWD,
|
|||
|
and null-terminate it. */
|
|||
|
memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
|
|||
|
cwd[cwd_len] = '\0';
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Special case for the root. */
|
|||
|
strlcpy (cwd, "/", cwd_size);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|