sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | 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 }