go4399 发表于 2023-11-14 11:09

修改文件目录时间的代码-touch

本帖最后由 go4399 于 2023-11-14 11:11 编辑

使用与linux下touch一致,部分功能没有实现。支持命令行下通配符,如touch \*.\*

```cpp
// touch.c
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int _dowildcard = 1; // 1 = support wildcard, 0 = not

#define CH_ATIME 1
#define CH_MTIME 2
#define CH_CTIME 4

#define TM_SET 8

static int change_times = 0;
static FILETIME atime;
static FILETIME ctime;
static FILETIME mtime;

void usage()
{
fputws(L"Usage: touch ... FILE...\n", stdout);
fputws(L"Update the access and modification times of each FILE to the current time.\n\n\
Mandatory arguments to long options are mandatory for short options too.\n\
-a                     change only the access time\n\
-c                     change only the creation time\n\
-m                     change only the modification time\n\
-r FILE                use this file's times instead of current time\n\
-t STAMP               use [YY]MMDDhhmm[.ss] instead of current time\n", stdout);
}

int use_reference(const wchar_t* fileName)
{
    HANDLE hFile;
    if (change_times & TM_SET)
    {
      return 0;
    }
    hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    if (hFile != INVALID_HANDLE_VALUE)
    {
      if (GetFileTime(hFile, &ctime, &atime, &mtime))
      {
            change_times |= TM_SET;
      }
      CloseHandle(hFile);
    }
    return change_times & TM_SET;
}

int use_stamp(wchar_t* opt)
{
    wchar_t* p;
    SYSTEMTIME st;
    if (change_times & TM_SET)
    {
      return 0;
    }
    GetLocalTime(&st);
    st.wMilliseconds = 0;
    p = wcschr(opt, L'.');
    if (p)
    {
      if (p == 0)
      {
            st.wSecond = _wtoi(p + 1);
            *p = 0;
      }
      else
      {
            return 0;
      }
    }
    else
    {
      st.wSecond = 0;
      p = opt + wcslen(opt);
    }
    if (p >= opt + 2)
    {
      p -= 2;
      st.wMinute = _wtoi(p);
      *p = 0;
      if (p >= opt + 2)
      {
            p -= 2;
            st.wHour = _wtoi(p);
            *p = 0;
            if (p >= opt + 2)
            {
                p -= 2;
                st.wDay = _wtoi(p);
                *p = 0;
                if (p >= opt + 2)
                {
                  p -= 2;
                  st.wMonth = _wtoi(p);
                  *p = 0;
                  if (p == opt)
                  {
                  }
                  else if (p == opt + 2)
                  {
                        p -= 2;
                        st.wYear = (st.wYear / 100) * 100 + _wtoi(p);
                  }
                  else if (p == opt + 4)
                  {
                        p -= 4;
                        st.wYear = _wtoi(p);
                  }
                  else
                  {
                        return 0;
                  }
                  if (SystemTimeToFileTime(&st, &ctime) && LocalFileTimeToFileTime(&ctime, &atime))
                  {
                        ctime = atime;
                        mtime = atime;
                        change_times |= TM_SET;
                  }
                }
            }
      }
    }
    return change_times & TM_SET;
}

int use_current()
{
    SYSTEMTIME st;
    if (change_times & TM_SET)
    {
      return 0;
    }
    GetSystemTime(&st);
    st.wMilliseconds = 0;
    if (SystemTimeToFileTime(&st, &atime))
    {
      ctime = atime;
      mtime = atime;
      change_times |= TM_SET;
    }
    return change_times & TM_SET;
}

int touch(const wchar_t* fileName)
{
    int ret = 0;
    HANDLE hFile = CreateFileW(fileName, FILE_WRITE_ATTRIBUTES, 0, 0, OPEN_EXISTING, 0, 0);
    if (hFile != INVALID_HANDLE_VALUE)
    {
      if (SetFileTime(hFile, change_times & CH_CTIME ? &ctime : 0, change_times & CH_ATIME ? &atime : 0, change_times & CH_MTIME ? &mtime : 0))
      {
            ret = 1;
      }
      CloseHandle(hFile);
    }
    return ret;
}

int wmain(int argc, wchar_t **argv)
{
    int ret = 1;
    int i;
    wchar_t* opt = 0;
    for (i = 1; i < argc; ++i)
    {
      opt = argv;
      if (opt == L'-')
      {
            if (opt != 0 && opt == 0)
            {
                if (opt == L'a')
                {
                  change_times |= CH_ATIME;
                }
                else if (opt == L'c')
                {
                  change_times |= CH_CTIME;
                }
                else if (opt == L'm')
                {
                  change_times |= CH_MTIME;
                }
                else if (opt == L'r')
                {
                  i++;
                  if (i < argc)
                  {
                        if (!use_reference(argv))
                        {
                            i = argc;
                            break;
                        }
                  }
                }
                else if (opt == L't')
                {
                  i++;
                  if (i < argc)
                  {
                        if (!use_stamp(argv))
                        {
                            i = argc;
                            break;
                        }
                  }
                }
                else
                {
                  i = argc;
                  break;
                }
            }
            else
            {
                i = argc;
                break;
            }
      }
      else
      {
            break;
      }
    }
    if (i == argc)
    {
      usage();
      return 0;
    }
    if (!(change_times & (CH_ATIME | CH_CTIME | CH_MTIME)))
    {
      change_times |= CH_ATIME | CH_CTIME | CH_MTIME;
    }
    use_current();
    for (; i < argc; ++i)
    {
      ret &= touch(argv);
    }
    return ret;
}

```

Batcher 发表于 2023-11-14 14:53

感谢分享。

UnxUtils for Windows 命令行工具包里面有一个 touch.exe 可以实现类似 Linux 里面的效果:
https://unxutils.sourceforge.net/

C:\Test\>touch --help
Usage: touch ... FILE...
Update the access and modification times of each FILE to the current time.

-a                     change only the access time
-c                     do not create any files
-d, --date=STRING      parse STRING and use it instead of current time
-f                     (ignored)
-m                     change only the modification time
-r, --reference=FILE   use this file's times instead of current time
-t STAMP               use MMDDhhmm[YY][.ss] instead of current time
      --time=WORD      access -a, atime -a, mtime -m, modify -m, use -a
      --help             display this help and exit
      --version          output version information and exit

STAMP may be used without -t if none of -drt, nor --, are used.

C:\Test\>touch --version
touch (GNU fileutils) 3.16

C:\Test\>dir 1.txt | findstr "1.txt"
2023/11/1119:36               207 1.txt

C:\Test\>touch 1.txt

C:\Test\>dir 1.txt | findstr "1.txt"
2023/11/1414:50               207 1.txt

CoreUtils for Windows 命令行工具包里面有一个 touch.exe 可以实现类似 Linux 里面的效果:
https://gnuwin32.sourceforge.net/packages/coreutils.htm

C:\Test\>touch --version
touch (GNU coreutils) 5.3.0

是开源的,可以下载它的C源代码。

go4399 发表于 2023-11-14 23:41

参考了https://ftp.gnu.org/gnu/coreutils/coreutils-9.4.tar.xz这个版本.
目的是能被win下的tinycc编译,通配符不支持UCRT

dudupangle 发表于 2023-11-15 09:10

大致看了一下。有如下问题:
1. 参数解析过于复杂了。建议优化。
2. 没有注释。
3. 函数中没有换行。分开的功能,加以添加上换行,提高可读性。
4. touch 函数逻辑有点小问题:
在CreateFileW失败的时候,返回0。0应该是代表成功。
5. 参数判断应该在参数解析之前。

go4399 发表于 2023-11-15 09:33

dudupangle 发表于 2023-11-15 09:10
大致看了一下。有如下问题:
1. 参数解析过于复杂了。建议优化。
2. 没有注释。


4. https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
CreateFileW Return value:
If the function succeeds, the return value is an open handle to the specified file, device, named pipe, or mail slot.
If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
返回0的情况,没有说明。

dudupangle 发表于 2023-11-15 11:58

go4399 发表于 2023-11-15 09:33
4. https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
CreateFileW ...

我的意思是你写的touch函数返回值的含义是什么。0成功,小于0失败?你现在的逻辑是如果CreateFileW失败,返回ret。ret的初值为0。我认为这个和你的设计不相符。

go4399 发表于 2023-11-15 14:21

本帖最后由 go4399 于 2023-11-15 14:24 编辑

dudupangle 发表于 2023-11-15 11:58
我的意思是你写的touch函数返回值的含义是什么。0成功,小于0失败?你现在的逻辑是如果CreateFileW失败, ...
touch函数,1表示成功,0表示失败
这个逻辑和原始coreutils版本一致

alwaysandforeve 发表于 2023-11-15 22:18

有个小建议:1.可以多写写注释,方便别人理解,更方便自己理解,要不等过段时间就忘了自己的想法了
我之前也写过这种,没几天就忘了很多具体的细节
页: [1]
查看完整版本: 修改文件目录时间的代码-touch