Thursday, October 22, 2009
Simple shell implementation
Just done a simple exercise in C programming and wrote a trivial implementation of Unix-style "shell" utility in C.
The source is here: http://code.google.com/p/inet-lab/source/browse/trunk/utils/Misc/myshell.c
All it does is basically accepts a user's command, parses it into "utility" and "arguments", does a basic sanity check, finds utility in $PATH, forks a child process and executes an utility with arguments via execv().
It also optionally supports GNU readline library and could be compiled e.g. with gcc like that :
gcc -g --pedantic –Wall [-DRL] -o myshell myshell.c [-lreadline]
Double quotes can be used as part of command line to pass a single argument with spaces. Other than that, no other shell substitution or expansion is done; in particular, there is no way to escape a double quote to pass it as part of an argument.
Why would anyone need this primitive shell?
Well, other that a simple training in a few basic C-programming concepts (also serving as a basic GNU readline example), it allows one to create a "back door" bypassing system security. Indeed, if such a "shell" is owned by root and is granted setuid privilege, it allows a regular user to execute any administrative/privileged command, a goal which cannot be accomplished with either regular system shells (which all have built-in protection against setuid flag) or any tools written in a scripting language; it must be a native system executable.
Labels: C, development, linux
Saturday, May 09, 2009
Magic of C preprocessor
Curtis Krauskopf discusses a neat trick which forces C preprocessor to merge __FILE__ and __LINE__ as one string, which isn't straightforward since __FILE__ is a string but __LINE__ is an integer.
It turns out you can solve the problem with two additional macros:
#include <stdio.h> #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define AT __FILE__ ":" TOSTRING(__LINE__) void error(const char *location, const char *msg) { printf("Error at %s: %s\n", location, msg); } int main(int , char**) { error(AT, "fake error"); return 0; }
Now, if only someone could find a way to merge __func__ and __LINE__ ....
Labels: C
Thursday, December 21, 2006
Portable thread library
Given how important multithreading programming has become, it is surprisingly difficult to find simple and usable portable C/C++ thead library.
In fact, this page claims there are only two such C++ libraries in existence: zthread and Boost.Threads.
zthread offers a clean, well-thought C++ design (claimed to be modeled after Java threads). Despite the fact that Cygwin was not mentioned as one of the supported platforms, it does compile and run under Cygwin with no visible (Cygwin-specific) problems. However,
- There is a complete absence of any examples, demos, unit tests, tutorials, etc. The only documentation provided is doxygen-generated.
- It appears that the implementation, rather than establishing a set of common wrappers over win32 and POSIX APIs (and perhaps others), is trying to more or less provide its own alternative implementation, perhaps having maximum platform-independence as one of the goals. As a result, library is quite volatile and does need a very active maintenance and user base, which does not seem to be there.
- As a typical example, some users report apparent problems under VC++ 6.0 which no one is able to diagnose or to comment on (thus one needs Visual .NET to use it).
- I spent long time trying to understand why my test program (under both Cygwin and Linux) was mysteriously crashing before I realized that zthread library is probably trying to do some very "intelligent" memory management, and as a result, all zthread-objects like mutex'es or threads must be dynamically allocated (via new); but then, you never need to release them...
So, is there a better alternative?
It appears that there is: it is POSIX Threads for Win32 project. They provide ready-to use libraries that can be (dynamically) linked with any Win32 application for (almost) complete POSIX thread support. See FAQ for long and interesting discussion on various ways to handle exceptions that arise from inside the library (in my experiments, I used pthreadVC1.lib)
Finally, a good pthread tutorial and documentation is available here.
Appendix. Sample C++ application implemented with zthread:
#include <string> #include <iostream> #include <zthread/Thread.h> #include <zthread/Mutex.h> ZThread::Mutex * output = new ZThread::Mutex (); class ThreadExample : public ZThread::Runnable { public: ThreadExample(std::string thread_name, size_t iterations) : name_(thread_name), num_times_to_loop_(iterations) {} void run() { for (size_t i = 0; i < num_times_to_loop_; i++) { output->acquire(); std::cerr << i << " " << name_ << "\n"; output->release(); } output->acquire(); std::cerr << name_ << " finished! " << std::endl; output->release(); } private: std::string name_; size_t num_times_to_loop_; }; int main() { using namespace std; using namespace ZThread; try { Thread t1(new ThreadExample("Thread-1", 50)); Thread t2(new ThreadExample("Thread-2", 50)); } catch (const Synchronization_Exception& e) { cerr << e.what() << "\n"; } }
and pthread:
#include <string> #include <iostream> #include <pthread.h> pthread_mutex_t output = PTHREAD_MUTEX_INITIALIZER; class ThreadExample { public: ThreadExample(std::string thread_name, size_t iterations) : name_(thread_name), num_times_to_loop_(iterations) {} void run() { for (size_t i = 0; i < num_times_to_loop_; i++) { pthread_mutex_lock (&output); std::cerr << i << " " << name_ << std::endl; pthread_mutex_unlock (&output); } pthread_mutex_lock (&output); std::cerr << name_ << " finished! " << std::endl; pthread_mutex_unlock (&output); } static void * prun(void * self) { ((ThreadExample *)self)->run(); return NULL; } private: std::string name_; size_t num_times_to_loop_; }; int main() { pthread_t p1, p2; pthread_create(&p1, NULL, ThreadExample::prun, new ThreadExample("Thread-1", 50)); pthread_create(&p2, NULL, ThreadExample::prun, new ThreadExample("Thread-2", 50)); pthread_exit(NULL); return 0; }
Labels: C, C++, multithreading, threads, windows
Sunday, December 10, 2006
double to float conversion
When we must convert float number to integer in C, we have two convenient functions at our disposal: floor and ceil. For some reason however, there are no natural counterparts to these functions when we deal with double -> float conversion.
I wrote a simple implementation of these utilities using existing C library functions frexp and ldexp:
float fdround ( double x, int isfloor ) { const int Nf = 23; /* IEEE 754: http://en.wikipedia.org/wiki/IEEE_754 */ double m; int exp, sx; if (0 == (sx = ((x < 0.0) ? (-1) : ((x > 0.0) ? 1 : 0)))) return 0.0; m = frexp ( x * sx, &exp ); return (float)(ldexp ( (double)sx * ((isfloor ^ (sx == -1))? floor : ceil) (m * (1 << (Nf + 1))), exp - Nf - 1)); } #define fceil(x) (fdround ( (x), 0 )) #define ffloor(x) (fdround ( (x), 1 ))
This implementation of course depends on the correct knowledge of the number of bits for mantissa as per IEEE standard, which is 23 for 32-bit floating-point numbers.
Here is a simple utility to test whether the above function generates correct numbers:
void test(double x) { float f = ffloor(x), c = fceil(x); float a = 0.4 * f + 0.6 * c, b = 0.6 * f + 0.4 * c, y = (f + c)/2; assert ( f <= x && c >= x ); assert ( a == c && b == f ); assert ( y == f || y == c ); }
Enjoy!
Labels: C
Thursday, June 22, 2006
printf() and 64-bit integers
In the last post we talked about scanf(), so perhaps it would be proper now to discuss printf().
Good and reasonably brief manual page could be found here; then make sure to check Microsoft-specific formats here.
Some interesting features are there, e.g. you can use <'> format (single quote) to enable locale-specific number output (has no effect in C locale); you can also refer to specific argument by number, specify format width as a separate argument, and so on.
Let's consider one specific task to print some (large) 64-bit value. The basic problem is that you must know (1) what (integer) type to use to represent (at least) 64-bit integers, and (2) what printf() format does this type correspond to, if any.
On UNIX platforms long usually correspond to "machine word" (that is to say, has same size as pointer) so on 64-bit UNIX long is 64-bit and the following code:
printf ( "2^63 = %lu\n", 1L << 63 );
will print 2^63 = 9223372036854775808.
On 32-bit UNIX systems long is 32-bit but long long is 64-bit, and then the following code will work as expected:
printf ( "2^63 = %llu\n", (long long)1 << 63 )
(Gnu gcc understands 1LL, but I am not sure if other compilers do).
On Windows, however (with Microsoft compiler) approach is different and size of basic types does not change with CPU and so long is always 32-bit (same as int). Thus, in older Visual C++ systems you have to write:
printf ( "2^63 = %I64u\n", (__int64)1 << 63 );
... while in Visual .NET the above example with "long long" will work.
Labels: 64-bit, C, visual studio
Tuesday, June 06, 2006
scanf
For the first time since I learned C 18 years ago I noticed that the standard library function scanf (and friends) has been enhanced with many new formats and features.
E.g., the following code
scanf ("%s %[^\n]\n", name, args);
reads a "word" from the input (and assigns it to name) and then reads the rest of the line till end-of-line, irrespective of spaces and other symbols, and assigns it to args.
Very detailed description of scanf formats is available at Solaris 10 Reference Manual Collection pages.
There is also a brief introduction to the standard C library here. Wikipedia, as usual, has a worthy collection of links as well.
Labels: C