2
\$\begingroup\$

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__ */
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Apr 9, 2022 at 8:24
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Why 20 in char str[20]? \$\endgroup\$ Commented Apr 9, 2022 at 15:36
  • \$\begingroup\$ what is wrong with 20? \$\endgroup\$ Commented Apr 10, 2022 at 9:30
  • 1
    \$\begingroup\$ I chose 20 to be large enough to store the directory name in the variable. But better modified with PATH_MAX. It's better this way? \$\endgroup\$ Commented Apr 11, 2022 at 16:29
  • 1
    \$\begingroup\$ lab.h deserves improvements, yet I assume it it only here as a reference. \$\endgroup\$ Commented Apr 11, 2022 at 21:40

1 Answer 1

3
\$\begingroup\$

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);
answered Apr 11, 2022 at 17:39
\$\endgroup\$
1
  • \$\begingroup\$ @Mark What overflow? If it relates to Detect string trouble, how does that not address the issue? \$\endgroup\$ Commented Apr 12, 2022 at 17:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.