For the beginner C++ programmer

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
 
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 ;)
 
Still though what's the best way to recurse directories? I've seen examples but all are poorly commented.... Including that code
 
ikbendeman said:
Still though what's the best way to recurse directories? ...

Perhaps using the ftw(3)() interface?

Code:
#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);
}
 
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.

Code:
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);
 
rolfheinrich said:
Perhaps using the ftw(3)() interface?

Code:
#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);
}

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.
 
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:

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

Code:
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);

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.
 
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.
 
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.

Code:
// 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?
 
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.
 
rolfheinrich said:
You are not letting us doing your home work, are you?



Code:
// 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?

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).
 
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)
 
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.
 
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).
 
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.
 
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.
 
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.
 
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.
 
rolfheinrich said:
You are not letting us doing your home work, are you?



Code:
// 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?

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?
 
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:

Code:
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.
 
Back
Top