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}