pwdx.c 2.97 KB
Newer Older
albert's avatar
albert committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// Copyright 2004 Nicholas Miell
//
// This file may be used subject to the terms and conditions of the
// GNU Library General Public License Version 2 as published by the
// Free Software Foundation.This program is distributed in the hope
// that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Library General Public License for more
// details.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>

#include "proc/version.h"

static void die(const char *msg) NORETURN;
static void die(const char *msg)
{
albert's avatar
albert committed
25
     fputs(msg, stderr);
albert's avatar
albert committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
     exit(1);
}

static void version(void) NORETURN;
static void version(void)
{
     printf("pwdx (%s)\n", procps_version);
     exit(0);
}

int main(int argc, char* argv[])
{
     regex_t re;
     int i;

     if (argc < 2)
          die("Usage: pwdx pid...\n");

     // Allowed on the command line:
     //
     // --version
     // -V
     // /proc/nnnn
     // nnnn
     //
     // where nnnn is any number that doesn't begin with 0.
     //
     // If --version or -V are present, further arguments are ignored
     // completely.
        
     regcomp(&re, "^((/proc/+)?[1-9][0-9]*|-V|--version)$",
             REG_EXTENDED|REG_NOSUB);

     for (i = 1; i < argc; i++) {
          if (regexec(&re, argv[i], 0, NULL, 0) != 0) {
61 62
               /* Constant 27 is the length of the error string "pwdx: ... " */
               char buf[27 + strlen (argv[i]) + 1];
albert's avatar
albert committed
63
               snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]);
64
               buf[sizeof(buf)-1] = '\0';
albert's avatar
albert committed
65 66 67 68 69 70 71 72
               die(buf);
          }
          if (!strcmp("-V", argv[i]) || !strcmp("--version", argv[i]))
               version();
     }

     regfree(&re);

73 74 75
     int alloclen = 128;
     char *pathbuf = malloc(alloclen);

albert's avatar
albert committed
76
     for (i = 1; i < argc; i++) {
77
          char * s;
albert's avatar
albert committed
78
          int len;
79 80
          /* Constant 10 is the length of strings "/proc/" + "/cwd" + 1 */
          char buf[10 + strlen(argv[i]) + 1];
albert's avatar
albert committed
81 82 83 84 85
          
          // At this point, all arguments are in the form /proc/nnnn
          // or nnnn, so a simple check based on the first char is
          // possible
          if (argv[i][0] != '/')
86
               snprintf(buf, sizeof buf, "/proc/%s/cwd", argv[i]);
albert's avatar
albert committed
87
          else
88
               snprintf(buf, sizeof buf, "%s/cwd", argv[i]);
albert's avatar
albert committed
89 90 91

          // buf contains /proc/nnnn/cwd symlink name on entry, the
          // target of that symlink on return
92 93 94 95 96 97
          while ((len = readlink(buf, pathbuf, alloclen)) == alloclen) {
               alloclen *= 2;
               pathbuf = realloc(pathbuf, alloclen);
          }

          if (len < 0) {
albert's avatar
albert committed
98 99
               s = strerror(errno == ENOENT ? ESRCH : errno);
          } else {
100 101
               pathbuf[len] = 0;
               s = pathbuf;
albert's avatar
albert committed
102 103 104 105 106 107 108
          }

          printf("%s: %s\n", argv[i], s);
     }

     return 0;
}