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