touch.c (2722B)
- // Collection of Unix tools, comparable to coreutils
- // SPDX-FileCopyrightText: 2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L // O_NOFOLLOW
- #include "../lib/iso_parse.h" /* iso_parse */
- #include <errno.h> /* errno */
- #include <fcntl.h> /* open */
- #include <stdbool.h> /* bool */
- #include <stdio.h> /* perror */
- #include <sys/stat.h> /* futimens, stat, utimensat */
- #include <unistd.h> /* getopt, opt*, close */
- int
- main(int argc, char *argv[])
- {
- bool ch_atime = false, ch_mtime = false;
- char *ref_file = NULL;
- struct timespec times[2] = {
- {.tv_sec = 0, .tv_nsec = UTIME_OMIT}, // access
- {.tv_sec = 0, .tv_nsec = UTIME_OMIT} // modification
- };
- struct timespec target = {0, UTIME_NOW};
- int open_flags = O_WRONLY | O_CREAT | O_NOCTTY;
- int utimensat_flags = 0;
- int c = 0;
- while((c = getopt(argc, argv, ":achmr:t:d:")) != -1)
- {
- switch(c)
- {
- case 'a':
- ch_atime = true;
- break;
- case 'c':
- open_flags ^= O_CREAT;
- break;
- case 'h':
- open_flags |= O_NOFOLLOW;
- utimensat_flags |= AT_SYMLINK_NOFOLLOW;
- break;
- case 'm':
- ch_mtime = true;
- break;
- case 'r':
- ref_file = optarg;
- break;
- case 't': // [[CC]YY]MMDDhhmm[.SS]
- // Too legacy of a format, too annoying to parse
- fprintf(stderr, "touch: Option -d not supported, use -t\n");
- return 1;
- break;
- case 'd':
- target = iso_parse(optarg);
- break;
- case ':':
- fprintf(stderr, "touch: Error: Missing operand for option: '-%c'\n", optopt);
- return 1;
- case '?':
- fprintf(stderr, "touch: Error: Unrecognised option: '-%c'\n", optopt);
- return 1;
- }
- }
- argc -= optind;
- argv += optind;
- // When neither -a nor -m are specified, change both
- if(!ch_atime && !ch_mtime)
- {
- ch_atime = true;
- ch_mtime = true;
- }
- if(ref_file == NULL)
- {
- if(ch_atime) times[0] = target;
- if(ch_mtime) times[1] = target;
- }
- else
- {
- struct stat ref;
- if(stat(ref_file, &ref) != 0)
- {
- perror("touch: stat");
- return 1;
- }
- if(ch_atime)
- {
- times[0] = ref.st_atim;
- }
- if(ch_mtime)
- {
- times[1] = ref.st_mtim;
- }
- }
- for(int i = 0; i < argc; i++)
- {
- int fd = open(argv[i], open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
- if(fd == -1)
- {
- if(errno == EISDIR)
- {
- if(utimensat(AT_FDCWD, argv[i], times, utimensat_flags) != 0)
- {
- perror("touch: utimensat");
- return 1;
- }
- return 0;
- }
- if(errno != ENOENT) perror("touch: open");
- return 1;
- }
- if(futimens(fd, times) != 0)
- {
- perror("touch: futimens");
- return 1;
- }
- if(close(fd) != 0)
- {
- perror("touch: close");
- return 1;
- }
- }
- return 0;
- }