master xplshn/aruu / cmd / posix / ln.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4
  5#include <errno.h>
  6#include <fcntl.h>
  7#include <libgen.h>
  8#include <string.h>
  9#include <unistd.h>
 10
 11#include "util.h"
 12
 13static void
 14usage(void)
 15{
 16  eprintf(
 17      "usage: %s [-f] [-L | -P | -s] target [name]\n"
 18      "       %s [-f] [-L | -P | -s] target ... dir\n",
 19      argv0,
 20      argv0
 21  );
 22}
 23
 24// ?man ln: make links between files
 25// ?man arguments: target [name]
 26// ?man create hard or symbolic links between files
 27int
 28main(int argc, char *argv[])
 29{
 30  char *targetdir = ".", *target = NULL;
 31  int   ret = 0, sflag = 0, fflag = 0, dirfd = AT_FDCWD, hastarget = 0, flags = AT_SYMLINK_FOLLOW;
 32  struct stat st, tst;
 33
 34  ARGBEGIN
 35  {
 36    // ?man -f: force creation of links by removing existing destination
 37    // files
 38    case 'f':
 39      fflag = 1;
 40      break;
 41    // ?man -L: specify option flag
 42    case 'L':
 43      flags |= AT_SYMLINK_FOLLOW;
 44      break;
 45    // ?man -P: specify option flag
 46    case 'P':
 47      flags &= ~AT_SYMLINK_FOLLOW;
 48      break;
 49    // ?man -s: make symbolic links instead of hard links
 50    case 's':
 51      sflag = 1;
 52      break;
 53    default:
 54      usage();
 55  }
 56  ARGEND
 57
 58  if (!argc)
 59    usage();
 60
 61  if (argc > 1) {
 62    if (!stat(argv[argc - 1], &st) && S_ISDIR(st.st_mode)) {
 63      if ((dirfd = open(argv[argc - 1], O_RDONLY)) < 0)
 64        eprintf("open %s:", argv[argc - 1]);
 65      targetdir = argv[argc - 1];
 66      if (targetdir[strlen(targetdir) - 1] == '/')
 67        targetdir[strlen(targetdir) - 1] = '\0';
 68    } else if (argc == 2) {
 69      hastarget = 1;
 70      target    = argv[argc - 1];
 71    } else {
 72      eprintf("%s: not a directory\n", argv[argc - 1]);
 73    }
 74    argv[argc - 1] = NULL;
 75    argc--;
 76  }
 77
 78  for (; *argv; argc--, argv++) {
 79    if (!hastarget)
 80      target = basename(*argv);
 81
 82    if (!sflag) {
 83      if (stat(*argv, &st) < 0) {
 84        weprintf("stat %s:", *argv);
 85        ret = 1;
 86        continue;
 87      } else if (fstatat(dirfd, target, &tst, AT_SYMLINK_NOFOLLOW) < 0) {
 88        if (errno != ENOENT) {
 89          weprintf("fstatat %s %s:", targetdir, target);
 90          ret = 1;
 91          continue;
 92        }
 93      } else if (st.st_dev == tst.st_dev && st.st_ino == tst.st_ino) {
 94        if (!fflag) {
 95          weprintf(
 96              "%s and %s/%s are the same "
 97              "file\n",
 98              *argv,
 99              targetdir,
100              target
101          );
102          ret = 1;
103        }
104        continue;
105      }
106    }
107
108    if (fflag && unlinkat(dirfd, target, 0) < 0 && errno != ENOENT) {
109      weprintf("unlinkat %s %s:", targetdir, target);
110      ret = 1;
111      continue;
112    }
113    if ((sflag ? symlinkat(*argv, dirfd, target) : linkat(AT_FDCWD, *argv, dirfd, target, flags))
114        < 0) {
115      weprintf("%s %s <- %s/%s:", sflag ? "symlinkat" : "linkat", *argv, targetdir, target);
116      ret = 1;
117    }
118  }
119
120  return ret;
121}