# For the beginner C++ programmer



## ikbendeman (Jan 6, 2013)

I've only done my introductory course in C++ programming, but I did get a 98% in the class so I like to think I can learn quickly. I'd like to know some good references for FreeBSD programming. Namely I have a portsearch tcsh script that I would like to re-write in C/C++ and I'm not too familiar with dirent.h or vectors but I would like the following: create an index of all the port directories and store information in arrays or vectors about the directories/files in the ports tree such as:
-path relative to /usr/ports (function for search, e.g. search for xorg returns x11/xorg)
-arrays/vectors that hold information such as does the port contain a pkg-message or pkg-plist
-possibly how to save this information into an "index" file that would be speedier than my current scripting use of find and awk
-I'd probably create a function accessed via a command line parameter so that I could create an alias to portsnap and everytime I wanted to 'portsnap fetch update' it would run this after and create a new index
-possible integration with portmaster through system() (I know, I know...)

Any information or references would be greatly appreciated


----------



## kpa (Jan 7, 2013)

There's ports-mgmt/psearch that is written in C++. It may be of some help. 

I recall it has a problem with non standard ports tree location, namely when /usr/ports is a symbolic link to somewhere else, maybe you can fix that


----------



## ikbendeman (Jan 11, 2013)

Still though what's the best way to recurse directories? I've seen examples but all are poorly commented.... Including that code


----------



## Anonymous (Jan 11, 2013)

ikbendeman said:
			
		

> Still though what's the best way to recurse directories? ...



Perhaps using the ftw(3)() interface?


```
#include <stdio.h>
#include <ftw.h>
#include <sys/stat.h>


int action(const char *path, const struct stat *st, int flags)
{
   printf("%10d%10d   %s\n", st->st_uid, st->st_gid, path);
   return 0;
}


int main(int argc, const char *argv[])
{
   return ftw("/etc", action, 16);
}
```


----------



## kpedersen (Jan 11, 2013)

What is wrong with just using dirent.h and coding the recursive walking manually?

A very well commented example (in C)...

http://www.lemoda.net/c/recursive-directory/index.html


----------



## nslay (Jan 13, 2013)

I think fts(3) is the easiest way to do this. You can look at find(1)'s source code as it uses fts(3).

EDIT:

Example: Visit all directories in /usr/ports recursively.


```
FTS *pFtsCtx;
FTSENT *pFtsEntry;

char * const pathv[2] = { "/usr/ports", NULL };

pFtsCtx = fts_open(pathv, FTS_NOSTAT, NULL);

while ((pFtsEntry = fts_read(pFtsCtx)) != NULL) {
  switch (pFtsEntry->fts_info) {
  case FTS_D:
    /* Process directory (just printed here) */
    puts(pFtsEntry->fts_path);
    break;
  }
}

fts_close(pFtsCtx);
```


----------



## nslay (Jan 13, 2013)

kpedersen said:
			
		

> What is wrong with just using dirent.h and coding the recursive walking manually?
> 
> A very well commented example (in C)...
> 
> http://www.lemoda.net/c/recursive-directory/index.html



Nothing. But since he's writing this for FreeBSD, he has ftw(3) and fts(3) at his disposal.


----------



## ikbendeman (Jan 13, 2013)

rolfheinrich said:
			
		

> Perhaps using the ftw(3)() interface?
> 
> 
> ```
> ...



I should iterate... I am a beginning (fairly basic) *C++* programmer. My course didn't teach us much *C*. The syntax is comprehensible but my understanding of printf() is lacking. I will do some reading tomorrow though; thank you.


----------



## ikbendeman (Jan 13, 2013)

nslay said:
			
		

> I think fts(3) is the easiest way to do this. You can look at find(1)'s source code as it uses fts(3).
> 
> EDIT:
> 
> ...



Is find not fairly slow though? Also I think I'd like to store an index in an array of C++ strings (or C strings if you guys think that would be faster/better... or maybe a vector of C++ strings.


----------



## kpa (Jan 13, 2013)

Avoid using C string format functions if you can, they are very error prone and many times open possibilities for buffer overflow exploits if used in critical system programs. The C++ string class should have all you need for safe string manipulation.


----------



## kpedersen (Jan 13, 2013)

nslay said:
			
		

> Nothing. But since he's writing this for FreeBSD, he has ftw(3) and fts(3) at his disposal.



So long as it is portable, it has my blessing :e


----------



## Anonymous (Jan 13, 2013)

ikbendeman said:
			
		

> I should iterate... I am a beginning (fairly basic) *C++* programmer. My course didn't teach us much *C*.



You are not letting us doing your home work, are you?



			
				ikbendeman said:
			
		

> The syntax is comprehensible but my understanding of printf() is lacking. I will do some reading tomorrow though; thank you.




```
// dirtree.cpp

#include <iostream>
#include <ftw.h>
#include <sys/stat.h>


int action(const char *path, const struct stat *st, int flags)
{
   std::cout << st->st_uid << "  " << st->st_gid << "  " << path << "\n";
   return 0;
}


int main(int argc, const char *argv[])
{
   return ftw("/etc", action, 1);
}
```

Is this better now?


----------



## Anonymous (Jan 13, 2013)

kpedersen said:
			
		

> So long as it is portable, it has my blessing :e



Would a function being in The GNU C Library satisfy your requirements on portability?

14.3 Working with Directory Trees​


----------



## nslay (Jan 13, 2013)

ikbendeman said:
			
		

> Is find not fairly slow though? Also I think I'd like to store an index in an array of C++ strings (or C strings if you guys think that would be faster/better... or maybe a vector of C++ strings.



fts(3) shouldn't be any slower than using dirent. Use FTS_NOSTAT if you don't need to query permissions, user and group ids ... this should make it much faster.

To store the vector of strings, just vDirectories.push_back(pFtsEntry->fts_path) instead of puts(pFtsEntry->fts_path) and there you have it.

EDIT:
Just so you know, the issue of efficiency with strings (particularly ones that are file paths) is so incredibly trivial that it isn't even anything you should be thinking seriously about.

Just use a std::vector of std::string.

Don't get C hacker syndrome. Be practical and write code that is easy to read.


----------



## nslay (Jan 13, 2013)

rolfheinrich said:
			
		

> You are not letting us doing your home work, are you?
> 
> 
> 
> ...



While I also like ftw(3), it doesn't allow you to pass an arbitrary argument pointer. Instead you have to expose your argument as a global variable. This is certainly OK if you don't plan to have several instances traversing directories.

To be fair, I understand that fts(3) is not necessarily thread safe (though maybe FTS_NOCHDIR fixes that).


----------



## nslay (Jan 13, 2013)

kpedersen said:
			
		

> So long as it is portable, it has my blessing :e



But he's writing this for ports ... it's already automatically not portable.


----------



## kpedersen (Jan 13, 2013)

Well since fts3 / ftw is portable between BSDs as I said earlier, I would be fine using it in my own software.

But without starting any wars, come to think of it, no, just being in GNU libc is not good enough for what I class as portable.

Why do you think ports collections should not be portable? (http://www.pkgsrc.org/#index4h1)


----------



## nslay (Jan 13, 2013)

kpedersen said:
			
		

> Well since fts3 / ftw is portable between BSDs as I said earlier, I am don't disagree with it.
> 
> But without starting any wars, come to think of it, no, just being in GNU libc is not good enough for what I class as portable.
> 
> Why do you think ports collections should not be portable? (http://www.pkgsrc.org/#index4h1)



pkgsrc is not ports although it is similar.

Ports probably could be made to be portable, but it's not and probably won't be in the foreseeable future.

EDIT:
portmaster isn't even available from OpenBSD ports (The Original Poster did mention using it). My guess is that OpenBSD ports is very different too.


----------



## kpedersen (Jan 13, 2013)

pkgsrc was originally based on the FreeBSD ports.

Admittedly the ports collections are now quite different between the BSDs but if there was a way to make portmaster portable between them, I am sure the developer would take it and certainly for what the OP wants to do, it doesn't seem to be specific to a certain ports implementation.

There is rarely any excuse for not writing portable code in anything you do and as for the topic title, writing portable code is one of the most important things to learn when starting out if you want to write correct and reusable code.

Just look at Gnome or Xfce. Just because the Linux developers don't care about portable code, the BSD versions have much less functionality (such as auto-mounting).


----------



## nslay (Jan 13, 2013)

kpedersen said:
			
		

> pkgsrc was originally based on the FreeBSD ports.
> 
> Probably the reason why pkgsrc or OpenBSD ports doesn't have a portmaster is because portmaster was not written in a portable way.
> 
> There is rarely any excuse for not writing portable code in anything you do and as for the topic title, writing portable code is one of the most important things to learn when starting out if you want to write correct and reusable code.



The application is for *FreeBSD ports*. Where would he reuse it? Yes, he could use the POSIX API ... but why? It's for *FreeBSD*! He has the FreeBSD API at his disposal. Why limit yourself? It's not even a practical consideration.

EDIT:
Furthermore, but being overly general (like supporting every ports-like package manager) tends to destroy software. It may even result in a design more complicated than it needs to be.


----------



## nslay (Jan 13, 2013)

kpedersen said:
			
		

> Just look at Gnome or Xfce. Just because the Linux developers don't care about portable code, the BSD versions have much less functionality (such as auto-mounting).



Because there are some things that POSIX cannot do. It's not necessarily the Linux developers' fault (though it probably is). But at some point, you run into an OS-specific or device-specific problem where there is no POSIX support and you have no choice but to use OS-specific API.

Besides, this is irrelevant. The original poster is writing software for a package management system specific to FreeBSD. It's not the same problem as Xfce or Gnome which are *supposed* to work on any Unix-like system.


----------



## ikbendeman (Jan 14, 2013)

rolfheinrich said:
			
		

> You are not letting us doing your home work, are you?



No semester is over. I did get a 98% but this class was just at a community college, it was windows based, and it didn't even get to multi-file programs. We basically learned pointers, classes, functions and control statements.... It left a lot to be desired. At my university (when I was there) I think I could have learned three times as much in a quarter than I did this whole semester and worst part they don't even offer a second C++ class so now I'm left to my own devices.


----------



## cpm@ (Jan 14, 2013)

A great programmer needs a good brain, this requirement allows you to get the benefits of self-taught. This is the ideal place to motivate properly. 

I think this requisite is abundant in this Community 

PS most  really f***** as writing code to be given initial values in their definitions is to read a lot of ordinary variables, structures, unions and arrays and verify that someone has written it before.


----------



## ikbendeman (Jan 15, 2013)

rolfheinrich said:
			
		

> You are not letting us doing your home work, are you?
> 
> 
> 
> ...



Yes it works, and I can modify it to work for me, but I don't understand it. what is st an object of and where is this created? why in the second argument of "return ftw() is action passed with no parameters?


----------



## kpa (Jan 15, 2013)

The object st is created by the caller ftw(3) and "passed by reference" by using an explicit pointer (the star in front of it in the argument list) to it. 

It could be passed by using C++ references, something like this:


```
int action(const char *path, const struct stat &st, int flags)
{
   std::cout << st.st_uid << "  " << st.st_gid << "  " << path << "\n";
   return 0;
}
```

That would however require a C++ interface to ftw(3) and the stat structure and there's only a C version available.


----------



## ikbendeman (Jan 15, 2013)

i mean i can read man pages like stat(2) but what does this int flags mean? where is this coming from? Confused.


----------



## ikbendeman (Jan 15, 2013)

kpa said:
			
		

> The object st is created by the caller ftw(3) and "passed by reference" by using an explicit pointer (the star in front of it in the argument list) to it.
> 
> It could be passed by using C++ references, something like this:
> 
> ...



No the pointers make sense to me, I tracked down the man page for stat. I don't know C but from what I know in C++ a struct and a class are very similar except structs are public by default (where C doesn't have classes and doesn't understand "private")... so their uses in C are obviously for different reasons but still... I know enough where I think I can manipulate it, I'd just like to UNDERSTAND WHY this works, not just borrow and cite your code because it works and write the rest. What good is that to me?


----------



## ikbendeman (Jan 15, 2013)

> return ftw("/etc", action, 1);



is that second parameter calling action() without parantheses. And seriously that 1 is irritating me. Completely no understanding of what it does or why.


----------



## kpa (Jan 15, 2013)

The second argument of ftw(3) is a pointer to a function that has the following prototype:


```
int (*fn)(const char *, const struct stat *, int)
```

The action() function in the above example matches this prototype. It gets called by  ftw(3) for every entry the tree walk encounters. This is a common technique called "call back functions" where the calling code can remain the same but the called functions can be changed by passing pointers to different functions, often to functions that are loaded from plugin DLLs.

Oh and the following are equivalent because a name of a function is equal to taking the address of the function:


```
return ftw("/etc", action, 1);

   return ftw("/etc", &action, 1);
```


----------



## ikbendeman (Jan 15, 2013)

I guess I really need to learn more about C if I want to be doing programming for FreeBSD. I thought C++ was the new standard? No schools around here teach C and the C++ classes do everything they can to ignore the C subset.


----------



## ikbendeman (Jan 15, 2013)

kpa said:
			
		

> The second argument of ftw(3) is a pointer to a function that has the following prototype:
> 
> 
> ```
> ...



Thank you that makes sense... That 1 at the end is still perplexing me. This thread is getting rediculous...


----------



## kpa (Jan 15, 2013)

It's the maxfds parameter for ftw(3).


```
The maxfds argument specifies the maximum number of file descriptors to
     keep open while traversing the tree.  It has no effect in this implemen-
     tation
```


----------



## ikbendeman (Jan 15, 2013)

reading ftw.h answered my own question. Now I just need to try to figure out how to store these into an array of strings and "grep" them. C seems so much more convoluted than C++.


----------



## ikbendeman (Jan 15, 2013)

I think maybe I'll study dirent.h... I can't get this to traverse /usr/ports and only show directories. I just don't get it and its frustrating me.


----------



## expl (Jan 15, 2013)

I remembered that long time ago I had written a simple class for recursive and reusable path indexing. I modified it slightly to match exactly what you need (to only index directories and not files). Here it is: http://pastebin.com/Mzkiv188

You run it as ./parser /usr/ports gtk it will first index /usr/ports then it will search through the index for directories that contain 'gtk' in their names and print them out. Simple and primitive, but I think thats what you are looking for. It lacks comments so if you have any questions just shoot them at me.


----------



## Anonymous (Jan 15, 2013)

ikbendeman said:
			
		

> I think maybe I'll study dirent.h... I can't get this to traverse /usr/ports and only show directories. I just don't get it and its frustrating me.



Yeah, in general a computer does not what you want it to do, but it does exactly what you ask it to do 

So, why don't you simply ask it to show only directories by using a simple if() statement:

if (S_ISDIR(st->st_mode))


```
// dirtree.cpp

#include <iostream>
#include <ftw.h>
#include <sys/stat.h>

int action(const char *path, const struct stat *st, int flags)
{
   if (S_ISDIR(st->st_mode))
      std::cout << st->st_uid << "  " << st->st_gid << "  " << path << "\n";
   return 0;
}


int main(int argc, const char *argv[])
{
   return ftw("/usr/ports", action, 1);
}
```


----------



## ikbendeman (Jan 20, 2013)

rolfheinrich said:
			
		

> Perhaps using the ftw(3)() interface?
> 
> 
> ```
> ...



Ok I would like to use ftw but it only seems to work (with this code) for folders under the root directory. 

"return ftw("/etc", action, 16);" works;
"return ftw("/usr", action, 16);" works;
"return ftw("/usr/ports", action, 16); does not work.

The man page also seems to have no information on what the 16 is or does either. I can write this using dirent.h and it makes sense to me, however this just frustrates me because it's either not well-documented enough, or I just don't get it....


----------



## ikbendeman (Jan 20, 2013)

rolfheinrich said:
			
		

> Yeah, in general a computer does not what you want it to do, but it does exactly what you ask it to do
> 
> So, why don't you simply ask it to show only directories by using a simple if() statement:
> 
> ...



There is no output of this code.... seems to me like ftw is not well-enough documented and I'm not the only one who doesn't understand it...


----------



## ikbendeman (Jan 20, 2013)

Welp... I wrote it to help teach myself more C/C++. dirent.h worked fine... still need help with ftw... seems like I could write a more concise program with it... if it actually worked on anything besides folders in the root directory.


----------



## kpedersen (Jan 20, 2013)

ikbendeman said:
			
		

> seems to me like ftw is not well-enough documented and I'm not the only one who doesn't understand it...



Yep, contrary to what nslay was suggesting, I suggest sticking with this generic portable code for this very reason, it is more common because it is used across multiple operating systems and so you will be able to find more accurate and supported documentation and will make for easier maintainance at a later date.

(You can also get a version of dirent.h for Windows)


----------



## Anonymous (Jan 20, 2013)

ikbendeman said:
			
		

> There is no output of this code.... seems to me like ftw is not well-enough documented and I'm not the only one who doesn't understand it...



The code is OK. Either you named the compiled binary test, or "/usr/ports" does not exist on the machine where you tested the code, or the user executing the binary doesn't have read permission on it.

Check the permissions, then try again:


```
# g++ dirtree.cpp -o dirtree && ./dirtree
0  0  /usr/ports
0  0  /usr/ports/.svn
0  0  /usr/ports/.svn/pristine
0  0  /usr/ports/.svn/pristine/1b
...
```


----------

