Skip to content

Instantly share code, notes, and snippets.

@ukdave
Created October 11, 2023 20:18
Show Gist options
  • Save ukdave/a571b314c21c292da613bb10e1ada38b to your computer and use it in GitHub Desktop.
Save ukdave/a571b314c21c292da613bb10e1ada38b to your computer and use it in GitHub Desktop.
Program to set the Date Added attribute of a file to it's modified date on macOS
/*
* This code was written by Stefan Schmidt from this Stack Exchange post:
* https://apple.stackexchange.com/questions/40941/how-to-set-date-added-metadata-in-mac-os-x-10-7-lion#answer-457371
*
* Tested by me on macOS Bug Sur 11.7.10
*
* To compile:
*
* gcc -o date_add_to_mod date_add_to_mod.c
*
* Call the program from the current directory with a list of files:
*
* ./date_add_to_mod file1 file2 file3
*/
#include <stdlib.h>
#include <string.h>
#include <sys/attr.h>
#include <unistd.h>
#include <stdio.h>
/*
* For a list of files set "Date Added" in
* macOS Finder to match "Date Modified"
*/
/*
* Get kMDItemDateAdded of path.
*
* Returns:
* • 0 on success
* • 1 if a system call failed: check errno
* • 2 if something else went wrong
*/
int get_date_modified(const char* path, struct timespec * out) {
attrgroup_t request_attrs = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_MODTIME;
struct attrlist request;
memset(&request, 0, sizeof(request));
request.bitmapcount = ATTR_BIT_MAP_COUNT;
request.commonattr = request_attrs;
typedef struct {
u_int32_t length;
attribute_set_t returned;
struct timespec modified;
} __attribute__((aligned(4), packed)) response_buf_t;
response_buf_t response;
int err = getattrlist(path, &request, &response, sizeof(response), 0);
if (err != 0) {
return 1;
}
if (response.length != sizeof(response)) {
// Need a different-sized buffer; but provided one of exactly required
// size?!
return 2;
}
if (response.returned.commonattr != request_attrs) {
// Didn’t get back all requested common attributes
return 2;
}
out->tv_sec = response.modified.tv_sec;
out->tv_nsec = response.modified.tv_nsec;
return 0;
}
/*
* Set kMDItemDateAdded of path.
*
* Returns:
* • 0 on success
* • 1 if a system call failed: check errno
*/
int set_date_added(const char* path, struct timespec in) {
attrgroup_t request_attrs = ATTR_CMN_ADDEDTIME;
struct attrlist request;
memset(&request, 0, sizeof(request));
request.bitmapcount = ATTR_BIT_MAP_COUNT;
request.commonattr = request_attrs;
typedef struct {
struct timespec added;
} __attribute__((aligned(4), packed)) request_buf_t;
request_buf_t request_buf;
request_buf.added.tv_sec = in.tv_sec;
request_buf.added.tv_nsec = in.tv_nsec;
int err = setattrlist(path, &request, &request_buf, sizeof(request_buf), 0);
if (err != 0) {
return 1;
}
return 0;
}
/*
* Set kMDItemDateAdded of path to kMDItemFSContentChangeDate
*
* Returns:
* • 0 on success
* • 1 if a path doesn't exist
* • 2 if a function call failed
*/
int date_added_to_modifed(char* path) {
int err;
if(access(path, F_OK) != 0) {
printf("error: %s doesn't exist\n", path);
return 1;
}
struct timespec out;
err = get_date_modified(path, &out);
if (err != 0) {
return 2;
}
struct timespec in;
in.tv_sec = out.tv_sec;
in.tv_nsec = out.tv_nsec;
err = set_date_added(path, in);
if (err != 0) {
return 2;
}
return 0;
}
/*
* Modify paths given as command line arguments
*
* Returns:
* • 0 on success
* • 1 if a path doesn't exist
* • 2 if something else went wrong
*/
int main(int argc, char **argv) {
if (argc >= 2) {
for (int i = 1; i < argc; ++i) {
char *path = argv[i];
int err = date_added_to_modifed(path);
if (err == 0) {
printf("%s\n", path);
}
else if (err == 2) {
printf("an unknown error occured\n");
return 2;
}
}
}
else {
printf("usage: date_add_to_mod file1 file2 ...\n");
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment