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