1
\$\begingroup\$

hi guys this script recursively searches for files with a certain extension and possibly in a directory passed by the terminal ....(In case no directory has been passed, search from the current one) I believe it works correctly but I wanted to ask you if you noticed any flaws and report them to me or just tell me that everything is ok ...thanksss

 #include<stdio.h>
 #include<sys/stat.h>
 #include<errno.h>
 #include<stdlib.h> 
 #include<dirent.h>    
 #include<stdarg.h>
 #include<limits.h>
 #include<string.h>
 #include<time.h>



 char * scrivi(const char * a, char * B) {

 char *targetdir = malloc(2048);
 strcpy(targetdir,a);
 strcat(targetdir,"/");
 strcat(targetdir,B);
 return targetdir;
 } 



 void ricor1(const char estensione[],const char nomedirectory[]){

 struct stat attr;
 char dbuf[PATH_MAX+1];
 DIR * fh ;
 struct dirent *fdata;
 struct stat buf;

 if((fh=opendir(nomedirectory))==NULL){
 perror("ERRORE 1");
 exit(errno);
 }


 while((fdata = readdir (fh))!=NULL){
 if(strcmp(fdata->d_name,".")==0){
 continue;
 }

 if(strcmp(fdata->d_name,"..")==0){
 continue;
  }


 char *percorso;
 percorso = scrivi(nomedirectory,fdata->d_name);
 if (fdata->d_type==DT_DIR){

 ricor1(estensione,percorso);
  }
 if (fdata->d_type == DT_REG && strstr(fdata->d_name, estensione)) {

realpath(percorso, dbuf);
printf("[%s]", dbuf);

stat(percorso, &attr);

printf("%s\n", ctime(&attr.st_mtime));

 }

free(percorso);
 }
closedir(fh);
 } 


int main(int argc, char *argv[]) {



if(argc==3){

printf("Checking existence directory.. \n");

DIR* dir=opendir(argv[2]);

if(dir){    
closedir(dir);        
ricor1(argv[1],argv[2]);

 } 

else if (ENOENT==errno){

printf("Directory doesn't exist\n");
  }

else{

printf("error"); 

     }   
 }


else if(argc==2){



ricor1(argv[1],"./");

  }
 }
\$\endgroup\$
4
  • 2
    \$\begingroup\$ "I believe it works correctly" What do you mean? Didn't you test your code? \$\endgroup\$ Commented May 20, 2020 at 14:57
  • \$\begingroup\$ it works, I wanted to know if there could be any errors that I had not considered ... \$\endgroup\$ Commented May 20, 2020 at 15:07
  • 4
    \$\begingroup\$ At least you should indent your code correctly to enhance readability. \$\endgroup\$ Commented May 20, 2020 at 15:30
  • \$\begingroup\$ Please keep in mind that the C programming language is not a scripting language, it is a compiled language, scripts run through an interpreter, compilers generate an executable file that can be run more quickly and efficiently. \$\endgroup\$
    – pacmaninbw
    Commented May 20, 2020 at 16:43

1 Answer 1

2
\$\begingroup\$

... if you noticed any flaws ....

Problems with scrivi()

char * scrivi(const char * a, char * B) {

 char *targetdir = malloc(2048);
 strcpy(targetdir,a);
 strcat(targetdir,"/");
 strcat(targetdir,B);
 return targetdir;
 } 

Magic number

Why 2048? Big enough - who knows? Makes more sense to find the size needed and allocate rather than risk undefined behavior of a buffer overflow with strcpy(targetdir,a);

strcpy(), strcat(), strcat()

Walking down the string 3 times. Bad enough to do twice. (With a larger code re-write, we could avoid all repetitive walks down a, that is far below.)

char * B better as const char * b

Use const.

No error checking

With a recursion function, out-of-memory is a concern.

Alternative

char * scrivi(const char *a, const char *b) {
  size_t a_len = strlen(a);    
  size_t b_len = strlen(b);    
  char *targetdir = malloc(a_len + 1 + b_len + 1);

  if (targetdir) {
    memcpy(targetdir, a, a_len);
    targetdir[a_len] = '/';
    memcpy(targetdir + a_len + 1, b, b_len + 1);
  }
  return targetdir;
} 

Re-write trick

Rather than repetitive malloc()/free(), allocate once ever for a working file path buffer.

size_t file_path_size = PATH_MAX * 2;  // Let us be generous.
char file_path = mallloc(file_path_size); 

Before code calls ricor1() the first time, fill in the directory path and pass how much used, how much total.

strcpy(file_path, argv[2]);
ricor1(argv[1], file_path, strlen(argv[2]), file_path_size);

When forming the the full path, rather than percorso = scrivi(nomedirectory, fdata->d_name); with its allocations, write at the right place.

int length = snprintf(file_path + used, file_path_size - used, "/%s", fdata->d_name);

When recursing, append the sub-directory name and re-curse with an updated length

ricor1(estensione, file_path, used + length, file_path_size);

Include ample size error checking.

Consider code only ever needs 1 file_path[] at a time.

\$\endgroup\$
4
  • \$\begingroup\$ stpcpy also works. \$\endgroup\$
    – S.S. Anne
    Commented May 21, 2020 at 22:03
  • \$\begingroup\$ @S.S.Anne Yes - could use that approach. Yet a crucial attribute of forming the file path in a recursive function is 1) never overwriting the buffer and 2) detecting when the buffer was insufficient. I find snprintf() useful in insuring #1 and useful to detecting #2 and more convenient than the strcpy() route. I also find compilers more often these days emit efficient code with *printf() and friends due to analyzability and so less concern about a tedious format string interpretation that is no longer there. IAC, it is big O that I watch. \$\endgroup\$
    – chux
    Commented May 21, 2020 at 22:17
  • \$\begingroup\$ No, st p cpy. There's also stpncpy. \$\endgroup\$
    – S.S. Anne
    Commented May 21, 2020 at 22:19
  • \$\begingroup\$ @S.S.Anne Aha, Yes. could use stpcpy() to remove the painter problem, That like strcpy(), strcat() still obliges other code to avoid buffer overflow and insufficient size detection. I'll look more into stpncpy(). They also has the short-coming of not existing in the standard C library - but of course neither does opendir() and friends. \$\endgroup\$
    – chux
    Commented May 21, 2020 at 22:24

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.