master xplshn/aruu / cmd / posix / ln.c
  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}