aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/touch.c
blob: 298a93656d9968a4e5de0349c4f30d9cf450791a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* touch.c : change timestamp of a file
 *
 * Copyright 2012 Choubey Ji <warior.linux@gmail.com>
 *
 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html 

USE_TOUCH(NEWTOY(touch, "acd:mr:t:[!dtr]", TOYFLAG_BIN))

config TOUCH
  bool "touch"
  default y
  help
    usage: touch [-amc] [-d DATE] [-t TIME] [-r FILE] FILE...

    Update the access and modification times of each FILE to the current time.

    -a	change access time
    -m	change modification time
    -c	don't create file
    -d	set time to DATE (in YYYY-MM-DDThh:mm:SS[.frac][tz] format)
    -t	set time to TIME (in [[CC]YY]MMDDhhmm[.ss] format)
    -r	set time same as reference FILE
*/

#define FOR_touch
#include "toys.h"

GLOBALS(
  char *time;
  char *file;
  char *date;
)

// Fetch access and/or modification time of a file
int fetch(char *file, struct timeval *tv, unsigned flags)
{
  struct stat st;

  if (stat(file, &st)) return 1;

  if (flags & FLAG_a) {
    tv[0].tv_sec = st.st_atime;
    tv[0].tv_usec = st.st_atim.tv_nsec/1000;
  }
  if (flags & FLAG_m) {
    tv[1].tv_sec = st.st_mtime;
    tv[1].tv_usec = st.st_mtim.tv_nsec/1000;
  }

  return 0;
}

void touch_main(void)
{
  struct timeval tv[2];
  char **ss;
  int flag, fd, i;

  // Set time from clock?

  gettimeofday(tv, NULL);

  if (toys.optflags & (FLAG_t|FLAG_d)) {
    char *s, *date;
    struct tm tm;
    int len;

    localtime_r(&(tv->tv_sec), &tm);

    // Set time from -d?

    if (toys.optflags & FLAG_d) {
      date = TT.date;
      i = strlen(date);
      if (i) {
        // Trailing Z means UTC timezone, don't expect libc to know this.
        if (toupper(date[i-1])=='Z') {
          date[i-1] = 0;
          setenv("TZ", "UTC0", 1);
          localtime_r(&(tv->tv_sec), &tm);
        }
        s = strptime(date, "%Y-%m-%dT%T", &tm);
        if (s && *s=='.' && isdigit(s[1])) {
          sscanf(s, ".%d%n", &i, &len);
          s += len;
          tv->tv_usec = i;
          while (len++ < 7) tv->tv_usec *= 10;
        }
      } else s = 0;

    // Set time from -t?

    } else {
      strcpy(toybuf, "%Y%m%d%H%M");
      date = TT.time;
      i = ((s = strchr(date, '.'))) ? s-date : strlen(date);
      if (i < 8 || i%2) error_exit("bad '%s'", date);
      for (i=0;i<3;i++) {
        s = strptime(date, toybuf+(i&2), &tm);
        if (s) break;
        toybuf[1]='y';
      }
      if (s && *s=='.' && sscanf(s, ".%2u%n", &(tm.tm_sec), &len) == 1) 
        s += len;
    }

    errno = 0;
    tv->tv_sec = mktime(&tm);
    if (!s || *s || errno == EOVERFLOW) perror_exit("bad '%s'", date);
  }
  tv[1]=tv[0];

  // Set time from -r?

  if (TT.file && fetch(TT.file, tv, FLAG_a|FLAG_m))
    perror_exit("-r '%s'", TT.file);

  // Ok, we've got a time. Flip -am flags so now it's the ones we _keep_.

  flag = (~toys.optflags) & (FLAG_m|FLAG_a);

  // Loop through files on command line
  for (ss=toys.optargs; *ss;) {
    if ((flag == (FLAG_m|FLAG_a) || !fetch(*ss, tv, flag)) && !utimes(*ss, tv))
      ss++;
    else if (toys.optflags & FLAG_c) ss++;
    else if (access(*ss, F_OK) && (-1 != (fd = open(*ss, O_CREAT, 0666)))) close(fd);
    else perror_msg("'%s'", *ss++);
  }
}