Writing our own UNIX commands: pwd

Trigger warning: I am annoyed by the amount of low quality tutorials on the internet, but then decided: if you can’t beat them, join them. Here you go.

If you want to start learning programming in C, it is a logical choice to look at the coreutils.

The GNU Core Utilities are the basic file, shell and text manipulation utilities of the GNU operating system. These are the core utilities which are expected to exist on every operating system.

https://www.gnu.org/software/coreutils/

This time we are going to look at one of the simplest of these commands: pwd. The full operation of this command can, of course, be found with man:

NAME
       pwd - print name of current/working directory

SYNOPSIS
       pwd [OPTION]...

DESCRIPTION
       Print the full filename of the current working directory.

       -L, --logical
              use PWD from environment, even if it contains symlinks

       -P, --physical
              avoid all symlinks

       --help display this help and exit

       --version
              output version information and exit

       If no option is specified, -P is assumed.

       NOTE:  your shell may have its own version of pwd, which usually supersedes the version de‐
       scribed here.  Please refer to your shell's documentation for details about the options  it
       supports.

AUTHOR
       Written by Jim Meyering.

REPORTING BUGS
       GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
       Report any translation bugs to <https://translationproject.org/team/>

COPYRIGHT
       Copyright © 2020 Free Software Foundation, Inc.  License GPLv3+: GNU GPL version 3 or later
       <https://gnu.org/licenses/gpl.html>.
       This is free software: you are free to change and redistribute it.  There is  NO  WARRANTY,
       to the extent permitted by law.

SEE ALSO
       getcwd(3)

       Full documentation <https://www.gnu.org/software/coreutils/pwd>
       or available locally via: info '(coreutils) pwd invocation'

In this post, we ignore the additional flags, and take the most common version of pwd, namely the one without flags. The output is as follows:

> pwd
/home/bowero

Without further ado, let’s get into it!

Note: If you’d like to look at the official implementation of pwd, you can do so on the official repository. A mirror can be found on Github.

Basic functionality

We will assume -P as the default flag. In the original code, the following is said about that:

/* POSIX requires a default of -L, but most scripts expect -P. 
Currently shells default to -L, while stand-alone 
pwd implementations default to -P.  */

Implementing getcwd()

Luckily for us, there is a C function for us to start with: getcwd(). This is the man page:

NAME
       getcwd, getwd, get_current_dir_name - get current working directory

SYNOPSIS
       #include <unistd.h>

       char *getcwd(char *buf, size_t size);

       char *getwd(char *buf);

       char *get_current_dir_name(void);

[...]

DESCRIPTION
       These functions return a null-terminated string containing an absolute pathname that is the
       current working directory of the calling process.  The pathname is returned as the function
       result and via the argument buf, if present.

       The  getcwd()  function copies an absolute pathname of the current working directory to the
       array pointed to by buf, which is of length size.

       If the length of the absolute pathname of the current working directory, including the ter‐
       minating  null  byte,  exceeds size bytes, NULL is returned, and errno is set to ERANGE; an
       application should check for this error, and allocate a larger buffer if necessary.

       As an extension to the POSIX.1-2001 standard, glibc's getcwd() allocates the buffer dynami‐
       cally  using  malloc(3)  if buf is NULL.  In this case, the allocated buffer has the length
       size unless size is zero, when buf is allocated as big as  necessary.   The  caller  should
       free(3) the returned buffer.

As you see, you only need to call getcwd() with two arguments:

  1. char *buf: The buffer to store the absolute pathname
  2. size_t size: The length of the buffer.

Setting the buf to NULL allocates the buffer dynamically. This sounds great, so let’s use that! If that’s the case, we have to set size to determine the length of the allocated buffer. However, setting this to zero, allocates the size dynamically.

This results in the following code:

int main(int argc, char const *argv[])
{
    char *cwd = getcwd(NULL, 0);
    puts(cwd);
    return 0;
}

Running this seems to work properly:

> ./pwd
/home/bowero/Documents/tests

Because the manual specifically mentions so, we have to free the buffer:

The caller should free(3) the returned buffer.

So let’s do that as well:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    char *cwd = getcwd(NULL, 0);
    puts(cwd);
    free(cwd);
    return 0;
}

Congratulations, you are now a professional Linux developer! Contribute something!

Leave a Reply

Your email address will not be published. Required fields are marked *