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 }