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
131
132
/* 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][frac] 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(TT.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];
  struct tm tm;
  char **ss, *date, *s;<--- The scope of the variable 'date' can be reduced.
  int flag, fd, i, len;<--- The scope of the variable 'fd' can be reduced.

  // Set time from clock?

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

  // Set time from -d?

  if (toys.optflags & (FLAG_t|FLAG_d)) {
    if (toys.optflags & FLAG_d) {
      date = TT.date;
      i = strlen(date);
      if (i && i < sizeof(toybuf)) {
        // Trailing Z means UTC timezone, don't expect libc to know this.
        if (toupper(date[i])=='Z') {
          putenv("TZ=UTC");
          strcpy(toybuf, date);
          toybuf[i] = 0;
          date = toybuf;
          gmtime_r(&(tv->tv_sec), &tm);
        }
        s = strptime(date, "%Y-%m-%dT%T", &tm);
        if (s && *s=='.') {
          sscanf(s, ".%d%n", &i, &len);<--- scanf without field width limits can crash with huge input data on some versions of libc.
          s += len;
          tv->tv_usec = i;
        }
      } else s = 0;

    // Set time from -t?

    } else {
      strcpy(toybuf, "%Y%m%d%H%M");
      date = TT.time;
      for (i=0;i<3;i++) {
        s = strptime(date, toybuf+(i&2), &tm);
        if (s) break;
        toybuf[1]='y';
      }
      if (s && *s=='.') {
        int count = sscanf(s, ".%2d%u%n", &(tm.tm_sec), &i, &len);<--- %u in format string (no. 2) requires 'unsigned int *' but the argument type is 'int *'.<--- scanf without field width limits can crash with huge input data on some versions of libc.

        if (count==2) tv->tv_usec = i;
        s += len;
      }
    }

    errno = 0;
    tv->tv_sec = mktime(&tm);
    if (!s || *s || errno == EOVERFLOW) {
      // Warn Indiana Jones the monkey died.
      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 (-1 != (fd = open(*ss, O_CREAT, 0666))) close(fd);
    else perror_msg("'%s'", *ss++);
  }
}