getcwd сократить домашнюю директорию

Рейтинг: 1Ответов: 2Опубликовано: 25.04.2015

Здравствуйте! Есть код

char cwd[1024];
getcwd(cwd, sizeof(cwd));

При вызове возвращает: /home/example/exampledir/. Как заменить /home/example на ~/, если это домашняя директория юзера.

Ответы

▲ 3Принят

Чуть развёрнутей:

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

char *
cwd2home( char * path, size_t path_length )
{
    char cwd[PATH_MAX];
    char * home = getenv( "HOME" );
    size_t home_length = strlen( home );

    getcwd( cwd, sizeof(cwd) );
    if( !strncmp( cwd, home, home_length ) )
    {
        strncpy( path, "~", path_length );
        strncat( path, cwd + home_length, path_length - sizeof("~") );
    }
    else
    {
        strncpy( path, cwd, path_length );
    }
    path[path_length-1] = 0;
    return path;
}

int
main()
{
    char path[PATH_MAX];
    printf( "%s\n", cwd2home(path, sizeof(path)) );
    return 0;
}
▲ 1

На всякий случай, (мне кажется так будет поаккуратней с проверками и пожалуй как-то даже соответствует man getcwd) я бы написал (название для функции лучше сами придумайте):

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

char *
get2cwd (char *buf, size_t size)
{
  if (buf = getcwd(buf, size)) {
    struct passwd *p = getpwuid(getuid());
    char *home;
    if (p && (home = p->pw_dir)) {
      int home_len = strlen(home), cwdlen = strlen(buf);
      if (home_len < cwdlen && buf[home_len] == '/' &&
          strncmp(buf, home, home_len) == 0) {
        memmove(buf + 1, buf + home_len, cwdlen - home_len + 1);
        *buf = '~';
      }
    } else 
      perror(p ? "no home" : "getpwuid(getuid())");
  }

  return buf;
}

Update

Про get_current_dir_name(), слова в manpage

If the environment variable PWD is set, and its value is correct, then that value will be returned

и сомнениях @klopp (см. комментарии), можно ли ее использовать.

Как и обещал вчера, посмотрел код из eglibc-2.11.1/io/getdirname.c

/* Return a malloc'd string containing the current directory name.
   If the environment variable `PWD' is set, and its value is correct,
   that value is used.  */

char *
get_current_dir_name (void)
{
  char *pwd;
  struct stat64 dotstat, pwdstat;

  pwd = getenv ("PWD");
  if (pwd != NULL
      && stat64 (".", &dotstat) == 0
      && stat64 (pwd, &pwdstat) == 0
      && pwdstat.st_dev == dotstat.st_dev
      && pwdstat.st_ino == dotstat.st_ino)
    /* The PWD value is correct.  Use it.  */
    return __strdup (pwd);

  return __getcwd ((char *) NULL, 0);
}

IMHO все очевидно. Если "." и getenv("PWD") это одно и то же оглавление, то берем ранее уже кем-то (ну, естественно, шеллом) добытый путь, иначе вызываем дорогостоящий системный вызов -- getcwd.
(Если всем понятно, почему дорогостоящий -- замечательно, если нет -- спрашивайте (и сами тоже ищите, а лучше вычисляйте правильный ответ) и обсуждайте здесь, а можно ли это ускорить.

А то, я смотрю что-то совсем тут начинающие хакеры исчезли. Впечатление, что уже ничто никому не интересно).