Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Starting with C
Miradil
Miradil

Posted on • Edited on • Originally published atmmzeynalli.dev

     

Starting with C

In this article we will start the first steps to rewritels command. If you want to know more about command, please read Part 1.

Note: Source codes can be found at:https://github.com/Miradils-Blog/linux-ls

The easy way

Actually, C provides a function to run any OS command, and print its output:system. From the example in the reference, we see that, we can just take arguments from shell and pass it to that function, and it is done. The following code is just a showcase, and is far from optimized:

#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(intargc,char*argv[]){charcommand[50]="ls ";for(inti=1;i<argc;++i){strcat(command,argv[i]);strcat(command," ");}system(command);return0;}
Enter fullscreen modeExit fullscreen mode

The first element ofargv is always the name of executable, which we do not need, as we replace it with originalls. The rest of the arguments are copied tocommand variable, separated by spaces, and run. We get the following output:

Output of snippet

That's it, tutorial is done.

Just Kidding

Of course, we are not done! We will still writels from scratch. Even though, this might be a way to test our code, we will use slightly different method to test our command, as it will be explained later.

First, let's setup the environment

Before we start developing, we will setup the environment to be flexible for the future and automate our jobs. We will havesrc folder for source files,include for header files andtest for tests. We will also have source and include files of unity (see later), so, any added file/framework should be easy for us to handle. For these,make will be used, and rules will be defined in correspondingMakefile file. You can learn more aboutmake fromofficial documentation. So, our initialMakefile would look like this:

CC= gccCFLAGS=-I ./include/SRC_DIRS= ./src/sources:=$(wildcard$(SRC_DIRS)/*.c)compile:$(CC)$(CFLAGS)-ols$(sources)
Enter fullscreen modeExit fullscreen mode

Short explanation:

  • We are defininggcc to be our compiler.
  • We are including all files inìnclude folder (now empty, for future header files).
  • We are collecting all source files using wildcard.
  • We are compiling all of them incompile rule.

Now, if we usemake:

Output of make

Perfect! Now we are done with setting up basic settings.

Writing unit tests

Okay, time to start unit tests!!! We will useUnity Test Framework to do unit testing. It is one of widely used testing frameworks alongside withCheck,Google Test etc. Just downloading source code, and putting it to the project folder is enough to make it work (that is also why it is portable).

Dummy test

So, let's right our first dummy unit test, just to configure everything, and make sure everything works:

#include"unity.h"#include<stdbool.h>voidsetUp(void){}voidtearDown(void){}voidtest_ls_flags(void){TEST_ASSERT_TRUE(true);}intmain(){UNITY_BEGIN();RUN_TEST(test_ls_flags);returnUNITY_END();}
Enter fullscreen modeExit fullscreen mode

We also need to add test rule toMakefile:

runtest:$(CC)$(CFLAGS)-otest.otest/output.c./unity/src/unity.c./test.o
Enter fullscreen modeExit fullscreen mode

Here is output, when we runmake runtest:

Output of make runtest

More realistic test unit

Sweet! Now, let's try to at least write the part where we get output of originalls, so we later compare it with output of ours. You could expect us to usesystem function. The only problem is that, that function just prints the output of command, instead of storing it somewhere. We could extend command to forward the output to some file (for ex.ls -la > output.txt) and read later from that file, however, it is inefficient. Instead, we will usepopen function, which creates pipe (yes, yes, it is pipe file type that we have seen in Part 1) between command process and our code. We will get both outputs and compare them:

voidtest_ls_flags(void){FILE*pipe;system("make");// run 'make' to generate executablechar*test_flags[]={"-l","-a","-la"};// list of flags to testcharerror_msg[100];intflag_cnt=sizeof(test_flags)/sizeof(char*);// get the number of flags// Make sizes large enough to store outputscharls_output[1024]={0};charour_output[1024]={0};charcommand[128];for(inti=0;i<flag_cnt;++i){// Run original ls command and capture outputsprintf(command,"ls %s",test_flags[i]);pipe=popen(command,"r");fread(ls_output,1,1024,pipe);pclose(pipe);// Run our code and capture outputsprintf(command,"./ls %s",test_flags[i]);pipe=popen(command,"r");fread(our_output,1,1024,pipe);pclose(pipe);// Comparesprintf(error_msg,"Failed flag: %s",test_flags[i]);TEST_ASSERT_EQUAL_STRING_MESSAGE(ls_output,our_output,error_msg);}}
Enter fullscreen modeExit fullscreen mode

Our code will loop through given flags and assert output of each flag. Ourls.c file is still the same, containing code to list withsystem command. That works for now, we just need to run basic test. So, output of the following is:

Output of updated make runtest

Conclusion

For now, we are done with unit tests. Of course, unit tests should check functions and their behavior, but for now, we are only checking the global output of our command. Later, we will add unit tests for more specific functions. You can get codes from thisrepository.

Top comments(3)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
pauljlucas profile image
Paul J. Lucas
C++ Jedi Master
  • Email
  • Location
    San Francisco Bay Area
  • Education
    University of Illinois at Urbana-Champaign
  • Work
    Retired Principal Software Engineer
  • Joined
  • You shouldnot use a fixed-sized array. It can easily overflow.
  • Youshould do:

    returnsystem(command);
  • strcat() in a loop is inefficient.

  • You shouldnot have acompile target. The first target should bels.

  • The targetshould depend on the sources so it's rebuiltonly when they change. (That's the entire point ofmake.)

CollapseExpand
 
mmzeynalli profile image
Miradil
  • Location
    Lund, Sweden
  • Education
    Lund University
  • Work
    Embedded Firmware Engineer | Masters at Lund University
  • Joined
• Edited on• Edited
  1. You are correct, I know, however, I just kept it, as it is not that relevant for the topic.

  2. True, because, it is the first part of the series, everything is handled later)

4-5: I will fix them, thanks for the comment

CollapseExpand
 
pauljlucas profile image
Paul J. Lucas
C++ Jedi Master
  • Email
  • Location
    San Francisco Bay Area
  • Education
    University of Illinois at Urbana-Champaign
  • Work
    Retired Principal Software Engineer
  • Joined

IMHO, whenever presenting code, it should be the best code possible so any reader even glancing at it, can use it as a good example — OR — you add a comment saying there's a caveat. If you're going to handle stuff later,say so. What you want to avoid is someone doing a web search looking for some code, glancing at inefficient code, and thinking it's exemplary. Such users likely won't actually read the rest of the article.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Lund, Sweden
  • Education
    Lund University
  • Work
    Embedded Firmware Engineer | Masters at Lund University
  • Joined

More fromMiradil

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp