I wrote a program that implements the mkdir
command with the -p
parameter to create the parent directly if it does not exist.
My question is, is this implementation optimal? What else could I improve or what problems does implementation have?
code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <limits.h>
#include "lab.h"
bool vFlag = 0;
bool pFlag = 0;
int main(int argc, char* argv[])
{
bool err = false;
int option;
// check for optional arguments
while((option = getopt(argc,argv,"vp")) !=-1)
switch(option)
{
case 'v':
vFlag = 1;
break;
case 'p':
pFlag = 1;
break;
}
if(argc == 1)
{
err_msg("missing operand\nTry 'mkdir --help' for more information.");
exit(1);
}
for(int i=optind; i<argc; i++)
{
if(pFlag)
{
char* pStart = argv[optind];
char* pCurrent = argv[optind];
char* pLast = strrchr(argv[optind], '/');
while(*pCurrent != '0円')
{
if (*pCurrent == '/' || *pCurrent == '.')
{
char str[20] = {0};
strncpy(str, pStart, pCurrent - pStart);
if((access(str, X_OK) != 0) && (access(str, F_OK) == 0))
{
err_msg("cannot create directory '%s': Permission denied", str);
exit(1);
}
else
{
mkdir(str, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (chdir(str) < 0)
{
perror("chdir");
}
}
pStart = pCurrent+1;
}
++pCurrent;
}
mkdir(pLast+1, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
return 0;
}
if(access(argv[i],F_OK) == 0)
{
err_msg("cannot create directory ‘%s’: File exists",argv[i]);
err = true;
}
else
{
if(mkdir(argv[i], S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
err_ret("cannot create directory '%s'",argv[i]);
exit(1);
}
else
if (vFlag)
printf("mkdir: created directory '%s'\n", argv[i]);
}
}
if(err == false)
exit(0);
else
exit(1);
}
lab.h content:
/* Our own header, to be included *after* all standard system headers */
#ifndef __LAB_H__
#define __LAB_H__
#include <sys/types.h> /* required for some of our prototypes */
#include <stdio.h> /* for convenience */
#include <stdlib.h> /* for convenience */
#include <string.h> /* for convenience */
#include <unistd.h> /* for convenience */
#define MAXLINE 4096 /* max line length */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/* default file access permissions for new files */
#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)
/* default permissions for new directories */
/* prototypes for our own functions */
char *path_alloc(int *); /* {Prog pathalloc} */
int open_max(void); /* {Prog openmax} */
void clr_fl(int, int); /* {Prog setfl} */
void set_fl(int, int); /* {Prog setfl} */
void err_dump(const char *, ...); /* {App misc_source} */
void err_msg(const char *, ...);
void err_quit(const char *, ...);
void err_ret(const char *, ...);
void err_sys(const char *, ...);
#endif /* __LAB_H__ */
1 Answer 1
Just a little feedback.
Documentation
Code deserves at least an overall description of its goal. Consider self documenting by incorporating a help
option that details the goal.
case 'h':
puts(
"An implementation of the `mkdir` command with `-p` parameter.\n"
"-p does this.\n"
"-v does that.\n"
"...\n"
"Come and listen to my story about a man named Jed ...\n"
"...\n"
"Lorem ipsum dolor sit amet, consectetur adipiscing elit ...\n");
Detect string trouble
OP's code neither efficiently nor robustly handles string formation. Use a buffer to handle the maximum expected path and detect excessive arguments.
// char str[20] = {0};
// strncpy(str, pStart, pCurrent - pStart);
char str[PATH_MAX+0]; // See note below
int length = snprintf(str, sizeof str, "%s", pStart);
if (length < 0 || (unsigned) length >= sizeof str) {
err_ret("Path too long '%.20s...'", pStart);
exit(1);
}
PATH_MAX
is apparently the maximum size, not string length as from strlen()
, so that value includes space for the null character. # chars in a path name including nul.
Sentence vs. lower case messages
Example:
// err_ret("cannot create directory '%s'",argv[i]);
err_ret("Cannot create directory '%s'.",argv[i]);
// ^ ^
After exit/return
, omit else
if(err == false)
exit(0);
// else
exit(1);
Or in this case:
exit(!!err);
-
\$\begingroup\$ @Mark What overflow? If it relates to Detect string trouble, how does that not address the issue? \$\endgroup\$chux– chux2022年04月12日 17:52:39 +00:00Commented Apr 12, 2022 at 17:52
char str[20]
? \$\endgroup\$20
to be large enough to store the directory name in the variable. But better modified withPATH_MAX
. It's better this way? \$\endgroup\$lab.h
deserves improvements, yet I assume it it only here as a reference. \$\endgroup\$