以下程序生成随机文件。默认情况下,它们是表单/tmp/rfile-XXX。某些选项允许用户指定将这些文件放置在何处(使用-d dir标志)或使用什么名称来生成(使用生成name-XXX的-n name标志),然后使用mkstemp(3),然后在本文的上下文中使用fopen(3),我对以下内容感兴趣:
if(print_flag == USER)来测试用户是否需要更详细的输出,或者必须多次测试dir_flag以确定其目录名称和长度。在第二个实现中,是否最好使用pthreads来创建每个文件,并为每个线程生成一个/dir/name-XXX列表以进行操作?fullname_len检查(我不记得变量/值)。还有什么可能出现错误,或者以其他方式造成问题?style(9)指南,我不想知道其他样式的标准/指南是否更好。我只想知道它是否一致,是否有利于代码的检查/阅读。/* FILE: mkrfiles.c
AUTHOR: Jordan Effinger
DATE: 1 May 2023
VERSION: 0
LICENSE: BSD-3 Clause
COPYRIGHT<2023><Jordan Effinger>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
COMMENT: This program was written as an exercise in argument parsing, error handling, user i/o and in printing verbose error messages. It was written on a MacOS 10.15.7, with Apple clang 12.0.0 on an x86_64 system.
*/
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>
#define DFLT 0
#define USER 1
void
print_error(char *, pid_t, char *, unsigned, unsigned);
unsigned uerrno = 0;
int
main(int argc, char **argv)
{
// Debugging information:
pid_t main_pid = getpid();
char *exec_name = calloc( strlen(argv[0]), sizeof(char) );
if(exec_name == NULL)
{
print_error(argv[0], main_pid, __FILE__, __LINE__, errno);
return(errno);
}
// Default settings:
const char *dir_dflt = "/tmp";
const char *name_dflt = "rfile";
const char *fullname_dflt = "/tmp/rfile-XXX";
const size_t dir_len_dflt = strlen(dir_dflt);
const size_t name_len_dflt = strlen(name_dflt);
const size_t fullname_len_dflt = strlen(fullname_dflt)+1;
// User set settings:
char *dir_user, *name_user;
size_t dir_len, name_len, fullname_len = 0;
unsigned long count_user;
int count_flag =0;
int dir_flag = 0;
int name_flag = 0;
int print_flag = 0;
int ch;
while( (ch = getopt(argc, argv, "c:d:hn:p")) != -1)
{
switch(ch)
{
case 'c':
count_flag = USER;
count_user = strtoul(optarg,NULL,0);
if(errno != 0)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
return(errno);
break;
}
break;
case 'd':
dir_flag = USER;
dir_len = strlen(optarg);
dir_user = calloc(dir_len, sizeof(char) );
if(dir_user == NULL)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
return(errno);
break;
}
stpncpy(dir_user, optarg, dir_len);
break;
case 'h':
printf("%s -cdhnp\n", exec_name);
printf("-c file count\n");
printf("-d dir name\n");
printf("-h help\n");
printf("-n file name template\n");
printf("-p print (verbose)\n");
return(-1);
break;
case 'n':
name_flag = USER;
name_len = strlen(optarg);
name_user = calloc(name_len, sizeof(char) );
if(name_user == NULL)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
return(errno);
break;
}
stpncpy(name_user, optarg, name_len);
break;
case 'p':
print_flag = USER;
break;
default:
print_error(exec_name, main_pid, __FILE__, __LINE__, EINVAL);
printf("Execute with -h for a usage menu.\n");
free(exec_name);
return(EINVAL);
break;
}
}
if(print_flag == USER)
{
printf("Determining filename length.\n");
}
if( dir_flag == USER && name_flag == USER)
{
fullname_len = dir_len + strlen("/") + name_len + strlen("-XXX") + 1;
}
else if(dir_flag == USER && name_flag == DFLT)
{
fullname_len = dir_len + strlen("/") + name_len_dflt + strlen("-XXX") + 1;
}
else if(dir_flag == DFLT && name_flag == USER)
{
fullname_len = dir_len_dflt + strlen("/") + name_len + strlen("-XXX") + 1;
}
else if(dir_flag == DFLT && name_flag == DFLT)
{
fullname_len = fullname_len_dflt;
}
if(print_flag == USER)
{
printf("Allocating space for filename: %zu = %zu(strlen) & %zu(char size)\n", fullname_len * sizeof(char), fullname_len, sizeof(char) );
}
char *fullname = calloc(fullname_len, sizeof(char));
if(fullname == NULL)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
free(dir_user);
free(name_user);
return(uerrno);
}
if(print_flag == USER)
{
printf("Building filename:\n");
}
if(dir_flag == USER)
{
stpncpy(fullname, dir_user, dir_len);
}
else
{
stpncpy(fullname, dir_dflt, dir_len_dflt);
}
strcat(fullname,"/");
if(name_flag == USER)
{
strncat(fullname, name_user, name_len);
}
else
{
strncat(fullname, name_dflt, name_len_dflt);
}
strncat(fullname, "-XXX", strlen("-XXX") );
FILE *fd;
if(count_flag == USER)
{
if(print_flag == USER)
{
printf("Creating %zu random files.\n", count_user);
}
for( unsigned long idx = 0; idx < count_user; idx++ )
{
mkstemp(fullname);
fd = fopen(fullname, "w");
if(fd == NULL)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
free(dir_user);
free(name_user);
free(fullname);
return(errno);
}
fclose(fd);
fullname[fullname_len-2] = 'X';
fullname[fullname_len-3] = 'X';
fullname[fullname_len-4] = 'X';
}
}
else
{
if(print_flag == USER)
{
printf("Creating random file.\n");
}
mkstemp(fullname);
fd = fopen(fullname, "w");
if(fd == NULL)
{
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
free(dir_user);
free(name_user);
free(fullname);
return(errno);
}
fclose(fd);
}
free(exec_name);
free(dir_user);
free(name_user);
free(fullname);
return(uerrno);
}
void
print_error(char *progname, pid_t main_pid, char *file, unsigned line, unsigned errnum)
{
fprintf(stderr, "[%s, %u] [%s, line:%u], Errno: %u, Error: %s\n",
progname, main_pid, file, line, errnum, strerror(errnum)
);
}
```发布于 2023-05-06 20:57:52
短期记忆和视野都很小.main()函数太大,做得太多,因此很难读懂。尽可能将小任务解释为纯函数。
一个例子如下:
static inline void usage (const char *exec_name)
{
printf ("%s -cdhnp\n"
"-c file count\n"
"-d dir name\n"
"-h help\n"
"-n file name template\n"
"-p print (verbose)\n",
exec_name);
}现在我们可以取代:
case 'h':
printf("%s -cdhnp\n", exec_name);
printf("-c file count\n");
printf("-d dir name\n");
printf("-h help\n");
printf("-n file name template\n");
printf("-p print (verbose)\n");
return(-1);
break;通过以下方式:
case 'h':
usage();
return EXIT_FAILURE;argv[0]被传递给print_error() in main(),但它从未被消毒过:
int
main (int argc, char **argv)
{
/* POSIX requires the calling program to pass in a non-null argv[0]. */
if (!argv) { // Add
complain();
}
}/*
if( dir_flag == USER && name_flag == USER)
{
fullname_len = dir_len + strlen("/") + name_len + strlen("-XXX") + 1;
}
else if(dir_flag == USER && name_flag == DFLT)
{
fullname_len = dir_len + strlen("/") + name_len_dflt + strlen("-XXX") + 1;
}
else if(dir_flag == DFLT && name_flag == USER)
{
fullname_len = dir_len_dflt + strlen("/") + name_len + strlen("-XXX") + 1;
}
else if(dir_flag == DFLT && name_flag == DFLT)
{
fullname_len = fullname_len_dflt;
}
*/
if (dir_flag == USER) {
size_t len = name_flag == USER ? name_len : name_len_dflt;
fullname_len = dir_len + strlen("/") + size + strlen("-XXX") + 1;
} else {
fullname_len = name_flag == USER ?
dir_len_dflt + strlen("/") + name_len + strlen("-XXX") + 1 :
fullname_len_dflt;
}
/*
fullname[fullname_len-2] = 'X';
fullname[fullname_len-3] = 'X';
fullname[fullname_len-4] = 'X';
*/
memcpy (fullname + fullname_len - 4, "XXX", 3);我不喜欢这样的冗馀:
free(exec_name);
free(dir_user);
free(name_user);
free(fullname);但我们可以通过一些宏观手段来清理:
#define Fn_apply(type, fn, ...) { \
void *stopper_for_apply = (int[]){0}; \
type **list_for_apply = (type*[]){__VA_ARGS__, stopper_for_apply}; \
for (size_t i = 0; list_for_apply[i] != stopper_for_apply; i++) \
fn(list_for_apply[i]); \
}
#define Free_all(...) Fn_apply(void, free, __VA_ARGS__);
/* @Credit: Ben Klemens */
/* Could the above have a non-standard practice? Perhaps, but it's one option. */现在我们可以简单地写:
Free_all (exec_name, dir_user, name_user, fullname);或者,如果这对您来说不是一个可行的解决方案,我们可以使用goto:
out_free_all:
free(exec_name);
free(dir_user);
free(name_user);
free(fullname);
return(errno);现在我们可以简单地写:
goto out_free_all;而不是在每次错误时重复对free()的所有调用。
类似的标签可用于:
print_error(exec_name, main_pid, __FILE__, __LINE__, errno);
free(exec_name);
return(errno);
break;这在几乎所有的switch()案例中都是重复的。
stderr:写入错误消息
// printf("Execute with -h for a usage menu.\n");
fputs ("Execute with -h for a usage menu.\n", stderr);stderr没有缓冲,stdout没有缓冲。
代码在调用errno之后对strtoul进行测试,但在此之前并不清除errno。
#if 0
count_user = strtoul(optarg,NULL,0);
if(errno != 0)
#else
errno = 0; // Add
count_user = strtoul(optarg,NULL,0);
if(errno != 0)
#endif因此,一旦调用printf()就足够了:
#if 0
printf("%s -cdhnp\n", exec_name);
printf("-c file count\n");
printf("-d dir name\n");
printf("-h help\n");
printf("-n file name template\n");
printf("-p print (verbose)\n");
#else
printf ("%s -cdhnp\n"
"-c file count\n"
"-d dir name\n"
"-h help\n"
"-n file name template\n"
"-p print (verbose)\n", exec_name);
#endifunsigned uerrno = 0;没有必要将uerrno声明在main()之外。
不应该修改的
const限定符:声明。
const-qualified的一个对象是只读的。
void
print_error(const char *progname, pid_t main_pid, const char *file, unsigned line, unsigned errnum)restrict可以允许选择优化:
void
print_error(const char *restrict progname, pid_t main_pid, const char *restrict file, unsigned line, unsigned errnum)由于这一职能仅用于这一翻译单位,因此应以内部联系的方式宣布:
static void print_error (print_error(const char *restrict progname, pid_t main_pid, const char *restrict file, unsigned line, unsigned errnum)
/* Yes, it could be marked inline too. But the compiler can take care of that. */const:#if 0
// Default settings:
const char *dir_dflt = "/tmp";
const char *name_dflt = "rfile";
const char *fullname_dflt = "/tmp/rfile-XXX";
#else
// Default settings:
const char *const dir_dflt = "/tmp";
const char *const name_dflt = "rfile";
const char *const fullname_dflt = "/tmp/rfile-XXX";
#endif的errno值。
return(errno); // ??我建议:
return EXIT_FAILURE;bool类型:包括:
#include <stdbool.h>我们可以声明类型为bool的变量,并使用true和false代替USER和DFLT,这两个变量并不是非常明确的。
#if 0
int count_flag =0;
int dir_flag = 0;
int name_flag = 0;
int print_flag = 0;
#else
bool count_flag = dir_flag = name_flag = print_flag = false;
#endif#if 0
mkstemp (fullname);
#else
if (mkstemp (fullname) == -1) {
complain ();
}
#endifprint_error()之前定义它,那么我们就不需要main()的前向声明。<sys/errno.h>,标准的<errno.h>就足够了。return看起来像一个函数调用。简单地说: return a;而不是:
return(a);stpncpy()是一个非标准函数.考虑使用strcpy() / strncpy()代替。sizeof(char)定义为1,因此可以忽略它。struct,我们可以将目录名和它们的长度组合在一起: struct dir_info {
const char *const dir_name;
const size_t len;
};fd通常用于保存文件描述符,而不是文件指针。考虑将其命名为stream或fp。stpncpy()返回指向dest中终止空字节的指针,如果dest不是以空终止,则返回dest + n。保存它并将其用于进一步的连接,而不是调用strcat()。https://codereview.stackexchange.com/questions/284843
复制相似问题