master xplshn/aruu / cmd / posix / touch.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4
  5#include <errno.h>
  6#include <fcntl.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <time.h>
 10#include <unistd.h>
 11
 12#include "util.h"
 13
 14static int             aflag;
 15static int             cflag;
 16static int             mflag;
 17static struct timespec times[2] = {{.tv_nsec = UTIME_NOW}};
 18
 19static void
 20touch(const char *file)
 21{
 22  int fd, ret;
 23
 24  if (utimensat(AT_FDCWD, file, times, 0) == 0)
 25    return;
 26  if (errno != ENOENT)
 27    eprintf("utimensat %s:", file);
 28  if (cflag)
 29    return;
 30  if ((fd = open(file, O_WRONLY | O_CREAT, 0666)) < 0)
 31    eprintf("open %s:", file);
 32  ret = futimens(fd, times);
 33  close(fd);
 34  if (ret < 0)
 35    eprintf("futimens %s:", file);
 36}
 37
 38static time_t
 39parsetime(char *str)
 40{
 41  time_t     now;
 42  struct tm *cur, t = {0};
 43  int        zulu = 0;
 44  char      *format;
 45  size_t     len = strlen(str);
 46
 47  if ((now = time(NULL)) == -1)
 48    eprintf("time:");
 49  if (!(cur = localtime(&now)))
 50    eprintf("localtime:");
 51  t.tm_isdst = -1;
 52
 53  switch (len) {
 54    /* -t flag argument */
 55    case 8:
 56      t.tm_year = cur->tm_year;
 57      format    = "%m%d%H%M";
 58      break;
 59    case 10:
 60      format = "%y%m%d%H%M";
 61      break;
 62    case 11:
 63      t.tm_year = cur->tm_year;
 64      format    = "%m%d%H%M.%S";
 65      break;
 66    case 12:
 67      format = "%Y%m%d%H%M";
 68      break;
 69    case 13:
 70      format = "%y%m%d%H%M.%S";
 71      break;
 72    case 15:
 73      format = "%Y%m%d%H%M.%S";
 74      break;
 75    /* -d flag argument */
 76    case 19:
 77      format = "%Y-%m-%dT%H:%M:%S";
 78      break;
 79    case 20:
 80      /* only Zulu-timezone supported */
 81      if (str[19] != 'Z')
 82        eprintf("Invalid time zone\n");
 83      str[19] = 0;
 84      zulu    = 1;
 85      format  = "%Y-%m-%dT%H:%M:%S";
 86      break;
 87    default:
 88      eprintf("Invalid date format length\n", str);
 89  }
 90
 91  if (!strptime(str, format, &t))
 92    eprintf("strptime %s: Invalid date format\n", str);
 93  if (zulu) {
 94    t.tm_hour += t.tm_gmtoff / 60;
 95    t.tm_gmtoff = 0;
 96    t.tm_zone   = "Z";
 97  }
 98
 99  return mktime(&t);
100}
101
102static void
103usage(void)
104{
105  eprintf(
106      "usage: %s [-acm] [-d time | -r ref_file | -t time | -T time] "
107      "file ...\n",
108      argv0
109  );
110}
111
112// ?man touch: change file timestamps
113// ?man update the access and modification times of files
114int
115main(int argc, char *argv[])
116{
117  struct stat st;
118  char       *ref = NULL;
119
120  ARGBEGIN
121  {
122    // ?man -a: print or show all entries
123    case 'a':
124      aflag = 1;
125      break;
126    // ?man -c: print count or perform stdout action
127    case 'c':
128      cflag = 1;
129      break;
130    // ?man -d: specify directory
131    case 'd':
132    // ?man -t:str: sort or specify timestamp
133    case 't':
134      times[0].tv_sec  = parsetime(EARGF(usage()));
135      times[0].tv_nsec = 0;
136      break;
137    // ?man -m: specify mode or limit
138    case 'm':
139      mflag = 1;
140      break;
141    // ?man -r:str: operate recursively
142    case 'r':
143      ref = EARGF(usage());
144      if (stat(ref, &st) < 0)
145        eprintf("stat '%s':", ref);
146      times[0] = st.st_atim;
147      times[1] = st.st_mtim;
148      break;
149    // ?man -T:num: specify option flag
150    case 'T':
151      times[0].tv_sec  = estrtonum(EARGF(usage()), 0, LLONG_MAX);
152      times[0].tv_nsec = 0;
153      break;
154    default:
155      usage();
156  }
157  ARGEND
158
159  if (!argc)
160    usage();
161  if (!aflag && !mflag)
162    aflag = mflag = 1;
163  if (!ref)
164    times[1] = times[0];
165  if (!aflag)
166    times[0].tv_nsec = UTIME_OMIT;
167  if (!mflag)
168    times[1].tv_nsec = UTIME_OMIT;
169
170  for (; *argv; argc--, argv++)
171    touch(*argv);
172
173  return 0;
174}