// -*- c -*- // @@PLEAC@@_NAME // @@SKIP@@ C/Posix/GNU // @@PLEAC@@_WEB // @@SKIP@@ http://www.gnu.org/software/libc/libc.html // @@PLEAC@@_2.7 //------------------------------------------------------------------ #include <stdlib.h> // rand() srand() ... /* rand() function is not guaranted to produce random values */ int random; // the same value between 0 and RAND_MAX // for every run random = rand(); ... //------------------------------------------------------------------ ... float random; // the same value between 0 and 1.0 // for every run random = (float) rand() / RAND_MAX; ... //------------------------------------------------------------------ ... #define N 42 ... int random; // the same value between 0 and N -1 // for every run random = (int)( N * (double)rand() / (RAND_MAX + 1.0) ); ... // @@PLEAC@@_2.8 //------------------------------------------------------------------ #include <stdlib.h> // rand() srand() #include <time.h> // time() ... int random; // seeds a new sequence of random number srand(time(NULL)); // a different value between 0 and RAND_MAX // for every run random = rand(); ... // @@PLEAC@@_2.9 //------------------------------------------------------------------ /* under Linux /dev/random or /dev/urandom */ #include <stdio.h> // FILE *, fopen(), fread(), fclose() ... FILE * fp; unsigned int random; if ((fp = fopen("/dev/random","r")) == NULL) { perror("fopen"); exit(1); } fread(&random, sizeof(random), 1, fp); printf("random number %u from /dev/random\n", random); fclose(fp); ... //------------------------------------------------------------------ // @@PLEAC@@_3.0 // NOTE: In order to keep the size / verbosity of code examples to a minimum, library function // calls will generally not include error checking and handling. Example: // // p = malloc(...); if ((p = malloc(...)) == NULL) // { // ... handle error ... // } // // However, any necessary, application-specific error-related code, will still be used. Code // examples will endeavour, wherever possible, to make use of: // // * C99 Features e.g. variable-length arrays, non-const aggregate initialisers // * GNU Extensions e.g. nested functions, statement expressions // // The aim of doing so is to reduce redundancy [i.e. copious examples of older / standard C // already exist] as well as enhance the information value of each PLEAC example. // // Another item worthy of note is the use of writeable 'static local storage' in many custom // functions. Whilst a commonly-used technique that makes functions self-contained, and easier // to use [which is precisely why it is used here], it is not viable in multi-threaded code; // examples need to be suitably modified to work in such code. The section, 'Printing a Date', // in Chapter 3: Dates and Times, discusses this issue, and provides illustrative examples. // // The GNU C Library provides extensive, if somewhat low-level, date / time functionality. The // relevant section of the manual may be found at: // // http://www.gnu.org/software/libc/manual/html_mono/libc.html#Date%20and%20Time // // Outline of some of the more important concepts: // // * Calendar time represented in three forms: // - Simple time [a.k.a. Epoch Seconds, seconds since Jan 1, 1970]; represented by the // 'time_t' type [generally implemented as a 32 bit integer] // - Broken-down time; represented by 'struct tm', having a field for each time component // - Formatted string; certain string formats are printable and parseable as valid calendar // times // // * Date manipulations are ordinarily performed using broken-down time form, and are converted // to / from this form as the need arises: // - Input // + simple -> broken-down: 'localtime' / 'gmtime' // + string -> broken-down: 'strptime' / 'getdate' // - Arithmetic // + broken-down -> simple: 'mktime' // - Output // + broken-down -> string: 'strftime', 'asctime' // // The above list shows that a date / time value might be either be read in as a string [then // parsed, and converted], or converted from a simple-time value [e.g. the 'time' and // 'gettimeofday' routines return the current date / time as a simple-time value]. Date // arithmetic can, of course, be performed using the component fields of a broken-time value, // but would, more commonly, be first converted to a simple-time value [via 'mktime'], the // relevant operations performed, and converted back. Date / time output is ordinarily in // string form, the conversion most likely performed using 'strftime' routine, but use of the // 'printf' family is also possible // // * Despite a few exceptions, the date / time library routines are well standardised [just // include the <time.h> header], so are available across platforms. The widely-implemented, // though *NIX-specific, routines include: // - 'gettimeofday', essentially a higher resolution [microseconds, possibly nanoseconds] // version of 'time' // - 'strptime' and 'getdate', both routines similar in functionlity to 'sscanf' but using // format specifications specialised for date / time handling // // Implementations of general purpose date routines [which are generally used in several // sections] appear here. Protoypes appear in each section in which they are used. To // successfully compile examples ensure the relevant code from this section is copied into // the example source file. struct tm mk_tm(int year, int month, int day, int hour, int minute, int second) { struct tm tmv = { .tm_hour = hour, .tm_min = minute, .tm_sec = second, .tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_isdst = -1 }; mktime(&tmv); return tmv; } struct tm mk_tm_unfilled(void) { // -1 value used to indicate 'unfilled' since zero is a legitimate value in some fields return ((struct tm) { -1, -1, -1, -1, -1, -1, -1, -1, -1 }); } struct tm mk_tm_zero(void) { return ((struct tm) { 0, 0, 0, 0, 0, 0, -1, -1, -1 }); } void show_tm(const struct tm* tmvptr) { int year = tmvptr->tm_year > -1 ? tmvptr->tm_year + 1900 : tmvptr->tm_year; int month = tmvptr->tm_mon > -1 ? tmvptr->tm_mon + 1 : tmvptr->tm_mon; printf("Y/M/D H:M:S -> %04d/%02d/%02d %02d:%02d:%02d\n", year, month, tmvptr->tm_mday, tmvptr->tm_hour, tmvptr->tm_min, tmvptr->tm_sec); printf("DOW: %02d\nDOY: %02d\nDaylight Saving: %02d\n", tmvptr->tm_wday, tmvptr->tm_yday, tmvptr->tm_isdst); fflush(stdout); } // Note: Equivalent of 'timegm' function implemented on *NIX platforms [code may be 'unpacked' // for compilers not supporting nested functions] using the more portable technique of changing, // temporarily, the TZ value time_t mktime_utc(struct tm* tmvptr) { const char NUL = '\0'; char tzold[32] = {NUL}, tznew[32] = {NUL}; void save_tz(void) { char* tz = getenv("TZ"); if (tz != NULL) strcpy(tzold, tz); } void restore_tz(void) { char* tz = (tzold[0] != NUL) ? strcat(strcpy(tznew, "TZ="), tzold) : "TZ"; putenv(tz); } save_tz(); putenv("TZ=UTC"); time_t utc = mktime(tmvptr); restore_tz(); return utc; } // ---- // The following helper functions are loosely based on the implementations found in the // corresponding section(s) of PLEAC-PHP. The 'mk_date_interval' function is notable for // several reasons: // * Heavy use of pointer manipulation to search and tokenise string contents; illustrative // of a faster, more lightweight, though considerably more complex, approach to this task // when compared with use of library functions like 'strtok', 'strstr' and 'strchr' // * Comprehensive example of both variable-argument handling, and of sensible nested function // use // * The 'parse_entry' nested function illustrates an approach that can be used for mimicing // named function parameters // * Use of a delimited string as a lookup table in the 'getvalue' nested function is mainly // illustrative. Better performance can be obtained by other means; if still opting for a // string-based lookup table approach, a 'perfect hash'-based technique would be ideal, // though would require much more code to implement // // This function, together with 'to_epoch' and 'from_epoch', make use of string parameters // to represent a keyword. In C this approach wouldn't ordinarily be used because such // information can most often be encode in integer form e.g. integer constants or enumerations, // and the processing of integers is dramatically faster and far more efficient than string // operations such as linear searching and comparision. However, the reason for adopting this // string-based approach is to mimic the beahviour of the PLEAC-PHP implementations, as well // as illustrate various C techniques such as pointer manipulation and variable argument // handling. // // As an aside, error checking is minimal in most of these functions, and could certainly be // improved. time_t mk_date_interval(const char* arg1, ...) { static const char EQ = '=', COMMA = ',', NUL = '\0'; static char buffer[16]; // ---- char* parse_entry(const char* entry, int* value) { char* p = (char*) entry; // Assumes: "key=value" form // Extract, and convert 'value' while (*p++ != EQ) if (*p == NUL) return NULL; *value = atoi(p); // Extract 'key', copy to buffer for return p = buffer; while (*entry != EQ) *p++ = *entry++; *p = NUL; return buffer; } // ---- int getvalue(const char* key) { // Lookup table implemented as a delimited string static const char* const TBL = "sec=1,min=60,hou=3600,day=86400,wee=604800"; // Perform table lookup [via linear search (slow) of string] char* p = (char*) strcasestr(TBL, key); if (!p) return 0; // Extract table value. Since table is in delimited string form, use pointer // manipulation to mark start and end locations of required substring [value for key]. // Since locations are in a string constant, NUL-termination cannot be performed // in-place, so substring is copied to a buffer for subsequent processing while (*p++ != EQ) ; char* q = p; while (*q != NUL) if (*q == COMMA) break; else ++q; memcpy(buffer, p, q - p); *(buffer + (q - p)) = NUL; return atoi(buffer); } // ---- int interval = 0, value; const char* key; // Extract values from 1st argument if (!(key = parse_entry(arg1, &value))) return 0; interval += value * getvalue(key); // Setup for variable argument handling, and extract values from each of these const char* arg; va_list ap; va_start(ap, arg1); while ((arg = va_arg(ap, const char*)) != NULL) { if (!(key = parse_entry(arg, &value))) return 0; interval += value * getvalue(key); } va_end(ap); return interval; } time_t to_epoch(const char* intvltype, double multiple) { return (time_t) floor(multiple * get_date_interval_value(intvltype)); } double from_epoch(const char* intvltype, time_t tv) { double interval = get_date_interval_value(intvltype); return (interval > 0.0) ? tv / interval : 0.0; } double get_date_interval_value(const char* intvltype) { double interval = 0.0; // What, no lookup table ;) ? switch (*intvltype) { case 'd' : interval = strncasecmp(intvltype, "day", 3) == 0 ? 86400.0 : 0.0; break; case 'h' : interval = strncasecmp(intvltype, "hou", 3) == 0 ? 3600.0 : 0.0; break; case 'm' : interval = strncasecmp(intvltype, "min", 3) == 0 ? 60.0 : 0.0; break; case 's' : interval = strncasecmp(intvltype, "sec", 3) == 0 ? 1.0 : 0.0; break; case 'w' : interval = strncasecmp(intvltype, "wee", 3) == 0 ? 604800.0 : 0.0; break; } return interval; } // ---- int doy(int year, int month, int day) { const int BASE = -1; // Zero base [i.e. 1st day is zero] assumed static const int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; if (month < 1 || month > 12) return -1; return BASE + cumdays[month - 1] + day + (is_leap_year(year) && month > 2 ? 1 : 0); } const char* dayname(int day) { static const char* dnams[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; if (day < 0 || day > 6) return NULL; return dnams[day]; } // ---- bool is_parseable_date(const char* date, const char* fmt, struct tm* tmvptr) { static char datebuf[128]; // Date / time string is parsed according to format specification; if it fails it can // be assumed a format or type error occurred if (strptime(date, fmt, tmvptr) != 0) { datebuf[0] = '\1'; // Attempt to generate a date / time string using the previously created broken-time // value; if it succeeds it can be assumed the broken-down value is sound, but further // validation is needed to ensure the value is truly 'valid' return !(strftime(datebuf, sizeof(datebuf), fmt, tmvptr) == 0 && datebuf[0] != '\0'); } return false; } bool is_leap_year(int year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } bool is_valid_hms(int hour, int minute, int second) { // Purely arbitrary choice; allows 24:00:00, but may be omitted if (hour == 24 && minute == 0 && second == 0) return true; if (hour > -1 && hour < 24) if (minute > -1 && minute < 60) if (second > -1 && second < 60) return true; return false; } bool is_valid_ymd(int year, int month, int day) { static const int mtbl[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // Purely arbitrary choice; may be modified or omitted if (year < 1970 || year > 2038) return false; if (month > 0 && month < 13 && day > 0 && day <= mtbl[month]) return true; if (day == 29 && month == 2 && is_leap_year(year)) return true; return false; } bool is_valid_tm(struct tm* tmvptr) { return is_valid_hms(tmvptr->tm_hour, tmvptr->tm_min, tmvptr->tm_sec) && is_valid_ymd(tmvptr->tm_year + 1900, tmvptr->tm_mon + 1, tmvptr->tm_mday) && mktime(tmvptr) != -1; } // ---- const char* mk_fmt_date(const char* fmt, const struct tm* tmvptr) { static char datebuf[64]; return (strftime(datebuf, sizeof(datebuf), fmt, tmvptr) == 0) ? NULL : datebuf; } // ---- double hms_to_frac(int hour, int min, int sec) { return (hour * 3600 + min * 60 + sec) / 86400.; } void frac_to_hms(double frac, int* hour, int* min, int* sec) { int seconds = floor(frac * 86400.); *hour = seconds / 3600; *min = (seconds - *hour * 3600) / 60; *sec = (seconds - (*hour * 3600 + *min * 60)); } // @@PLEAC@@_3.1 // The current date may be obtained by two means: // * 'time' library function call; this is the standard, cross-platform approach, which // returns a value in Epoch Seconds [elapsed seconds since Jan 1, 1970] // * 'gettimeofday' library function; *NIX-specific approach which is similar to 'time' // function, but also allows values retrieveable to microsecond resolution // // Once an epoch second value is so obtained it is usual to convert it to a broken-down time // representation for subsequent manipulation, particularly conversion to string form for // output. If however, date arithmetic is to be performed, it is more common to see it done // directly in epoch second form #include <time.h> int main(void) { // Current time: epoch seconds time_t curtime = time(NULL); // Current time: broken-down time struct tm* locptr = localtime(&curtime); } // ------------ #include <time.h> #include <sys/time.h> int main(void) { // Current time: epoch seconds time_t curtime = ({ struct timeval timev; gettimeofday(&timev, NULL); timev.tv_sec; }); // Current time: broken-down time struct tm* locptr = localtime(&curtime); } // ---------------------------- #include <stdio.h> #include <time.h> int main(void) { time_t curtime = time(NULL); // Print standard format date string generated from a simple-time value printf("today is %s", ctime(&curtime)); fflush(stdout); struct tm* locptr = localtime(&curtime); // Print standard format date string generated from a broken-time value printf("today is %s", asctime(locptr)); fflush(stdout); // Use 'strftime' and date format specification to generate date string char datebuf[32]; strftime(datebuf, sizeof(datebuf), "%Y-%m-%d", locptr); printf("today is %s\n", datebuf); fflush(stdout); // Extract broken-down time components, and format string using 'printf' printf("today is %04d-%02d-%02d\n", locptr->tm_year + 1900, locptr->tm_mon + 1, locptr->tm_mday); fflush(stdout); } // @@PLEAC@@_3.2 // The 'mktime' library function fulfils this role by converting a broken-down time value to // the required epoch seconds. By default the generated value is local time based; a custom // function is provided for UTC-based time #include <time.h> #include <stdlib.h> #include <string.h> struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); time_t mktime_utc(struct tm* tmvptr); // ---- int main(void) { // Create a broken-down time value from arbitrary component values [Y,M,D,H,M,S] struct tm tmv = mk_tm(2007, 5, 2, 0, 0, 0); // Convert a broken-down time value to epoch seconds [local time assumed] time_t epoch_seconds_local = mktime(&tmv); // Convert a broken-down time value to epoch seconds [UTC time] time_t epoch_seconds_utc = mktime_utc(&tmv); } // @@PLEAC@@_3.3 // Two library functions fulfil this role: // * 'localtime', generates a local time-based value // * 'gmtime', as above, except generated value is UTC-based #include <time.h> #include <stdio.h> int main(void) { // Current time: epoch seconds time_t curtime = time(NULL); // Current time: broken-down time [local time-based] struct tm* locptr = localtime(&curtime); // Current time: broken-down time [UTC-based] struct tm* utcptr = gmtime(&curtime); printf("Dateline: %02d:%02d:%02d-%04d/%02d/%02d\n", locptr->tm_hour, locptr->tm_min, locptr->tm_sec, locptr->tm_year + 1900, locptr->tm_mon + 1, locptr->tm_mday); struct tm loct = *locptr; printf("Dateline: %02d:%02d:%02d-%04d/%02d/%02d\n", loct.tm_hour, loct.tm_min, loct.tm_sec, loct.tm_year + 1900, loct.tm_mon + 1, loct.tm_mday); fflush(stdout); } // @@PLEAC@@_3.4 // This task entails the conversion of all dates to be so treated to epoch seconds, which can // then be arithmetically manipulated. A number of helper functions are included: // * 'to_epoch', 'from_epoch'; convert a time interval e.g. day, week etc to / from epoch second // intervals // * 'mk_date_interval', creates an epoch second interval from a set of disparate time intervals // * 'frac_to_hms' and 'hms_to_frac' convert fractional days to / from H:M:S values // // In general, arithmetic manipulation of 'time_t' values is safe, but on platforms where it // may not be implemented as an 'int' or related type, the 'difftime' library function should // used #include <stdio.h> #include <stdarg.h> #include <string.h> #include <math.h> #include <time.h> time_t mk_date_interval(const char* arg1, ...); double get_date_interval_value(const char* intvltype); time_t to_epoch(const char* intvltype, double multiple); double from_epoch(const char* intvltype, time_t tv); const char* mk_fmt_date(const char* fmt, const struct tm* tmvptr); struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); // ---- int main(void) { time_t now, difference; time_t when = now + difference; // 'difference' epoch seconds in the future time_t then = now - difference; // 'difference' epoch seconds in the past // ------------ now = time(NULL); time_t diff1 = to_epoch("day", 2), diff2 = to_epoch("week", 2); printf("Today is: %s", ctime(&now)); printf("Two days in the future is: %s", ({ time_t tv = now + diff1; ctime(&tv); })); printf("Two weeks in the past is: %s", ({ time_t tv = now - diff2; ctime(&tv); })); printf("\n"); fflush(stdout); printf("Today is: %s\n", mk_fmt_date("%Y-%m-%d", localtime(&now))); printf("Two days in the future is: %s\n", ({ time_t tv = now + diff1; mk_fmt_date("%Y-%m-%d", localtime(&tv)); })); printf("Two weeks in the past is: %s\n", ({ time_t tv = now - diff2; mk_fmt_date("%Y-%m-%d", localtime(&tv)); })); printf("\n"); fflush(stdout); // ------------ time_t birthtime = 96176750; // 18/Jan/1973, 3:45:50 am time_t interval = 5 + // 5 seconds 17 * 60 + // 17 minutes 2 * 60 * 60 + // 2 hours 55 * 60 * 60 * 24; // and 55 days then = birthtime + interval; // Then is Wed Mar 14 06:02:55 1973 printf("Then is %s", ctime(&then)); fflush(stdout); // ------------ birthtime = ({ struct tm tmv = mk_tm(1973, 1, 18, 3, 45, 50); mktime(&tmv); }); interval = mk_date_interval("day=55", "hou=2", "min=17", "sec=5", NULL); then = birthtime + interval; printf("To be precise: %s\n", mk_fmt_date("%H:%M:%S, %Y-%m-%d", localtime(&then))); fflush(stdout); } // @@PLEAC@@_3.5 // Refer to explanation in previous section #include <stdio.h> #include <string.h> #include <math.h> #include <time.h> double get_date_interval_value(const char* intvltype); time_t to_epoch(const char* intvltype, double multiple); double from_epoch(const char* intvltype, time_t tv); double hms_to_frac(int hour, int min, int sec); void frac_to_hms(double frac, int* hour, int* min, int* sec); struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); // ---- int main(void) { time_t recent, earlier; time_t seconds = recent - earlier; // 'seconds' is epoch seconds interval // ------------ struct tm tmv1 = mk_tm(1982, 4, 12, 12, 0, 0), tmv2 = mk_tm(1981, 4, 12, 6, 0, 0); time_t interval = mktime(&tmv1) - mktime(&tmv2); double days = from_epoch("day", interval); printf("An interval of %d epoch seconds is %.3f days\n", interval, days); // ------------ time_t bree = 361535725; // 16 Jun 1981, 4:35:25 [actually: 20:35:25] time_t nat = 96201950; // 18 Jan 1973, 3:45:50 [actually: 21:45:50] time_t difference = bree - nat; // Or do: difference = difftime(bree, nat); printf("There were %d seconds between Nat and Bree\n", difference); fflush(stdout); // ---- // Or do following to ensure correctly adjusted intervals are generated: bree = ({ struct tm tmv = mk_tm(1981, 6, 16, 20, 35, 25); mktime(&tmv); }); nat = ({ struct tm tmv = mk_tm(1973, 1, 18, 21, 45, 50); mktime(&tmv); }); difference = bree - nat; printf("There were %d seconds between Nat and Bree\n", difference); fflush(stdout); // ------------ printf("There were %.3f seconds between Nat and Bree\n", from_epoch("sec", difference)); printf("There were %.3f minutes between Nat and Bree\n", from_epoch("min", difference)); printf("There were %.3f hours between Nat and Bree\n", from_epoch("hour", difference)); printf("There were %.3f days between Nat and Bree\n", from_epoch("day", difference)); printf("There were %.3f weeks between Nat and Bree\n", from_epoch("week", difference)); fflush(stdout); // ------------ double frac = ({ double days = from_epoch("day", difference); days - floor(days); }); int hour, min, sec; frac_to_hms(frac, &hour, &min, &sec); printf("Bree came %d days, %d:%d:%d after Nat\n", (int) from_epoch("day", difference), hour, min, sec); fflush(stdout); } // @@PLEAC@@_3.6 // These are obtainable by generating a broken-down time value, and either: // * Computing the required item using data from the relevant 'tm_' fields // * Calling the 'strftime' library function with the broken-down time value, and // appropriate format specification. Examples use 'mk_fmt_date' which makes use of // 'strftime' // It should be noted that each item may have several possible values, so care is needed // in interpreting results #include <stdbool.h> #include <time.h> #include <stdio.h> const char* mk_fmt_date(const char* fmt, const struct tm* tmvptr); struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); bool is_leap_year(int year); int doy(int year, int month, int day); const char* dayname(int day); // ---- int main(void) { // Current time in broken-down time form struct tm* locptr = ({ time_t curtime = time(NULL); localtime(&curtime); }); int day_of_week, day_of_year, week_of_year; day_of_week = atoi(mk_fmt_date("%w", locptr)); // 1st DOW: sun=0 -> sat=6 day_of_week = atoi(mk_fmt_date("%u", locptr)); // 1st DOW: mon=1 -> sun=7 day_of_week = locptr->tm_wday; // " " " day_of_year = atoi(mk_fmt_date("%j", locptr)); // 1 -> 366 day_of_year = locptr->tm_yday; // 0 -> 365 day_of_year = doy(2007, 5, 12); // " -> " week_of_year = atoi(mk_fmt_date("%U", locptr)); // 0 -> 53; sun 1st day of week 1; // preceding days week 0 week_of_year = atoi(mk_fmt_date("%V", locptr)); // 1 -> 53; ISO week_of_year = atoi(mk_fmt_date("%W", locptr)); // 0 -> 53; mon 1st day of week 1; // preceding days week 0 // ------------ int year = 1981, month = 6, day = 16; struct tm tmv = mk_tm(year, month, day, 0, 0, 0); printf("%d/%d/%d was a %s in week %s\n", month, day, year, dayname(tmv.tm_wday), mk_fmt_date("%V", &tmv)); } // @@PLEAC@@_3.7 // Parsing date / time values from strings generally sees: // * Use of the 'strptime' / 'getdate' functions on *NIX platforms // * Use of 'sscanf' // * Custom routines [e.g. strtok'-based, regex-based, raw pointer manipulations] // // to extract date / time components and create broken-down time values from them. The first // approach is probably the simplest, but is not possible on all platforms. It is also // interesting that it includes only minimal validation capability, that is, while format and // type violations are readily detected [e.g. supplying either 2007-12-12, or aaaa/bb/cc, when // something like 2007/12/12 is expected], date validity is only minimally checked [e.g. // invalid day-month combinations are allowed, as are unrepresentable years (like 1111)], so // must be manually implemented. A small set of validation and helper functions is implemented // below. // // Note: 'getdate' is a high-level function built using top of 'strptime'. It will not be // discussed here, so refer to GNU C Library documentation for details. // 1. 'strptime' Example [conventional use of 'strptime'] #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { const char* date = "1998-06-03"; const char* FMT = "%Y-%m-%d"; if (strptime(date, FMT, &tmv) == 0) { fputs("Date parse error ...\n", stderr); return EXIT_FAILURE; } struct tm tmv = { 0, 0, 0, 0, 0, 0, -1, -1, -1 }; time_t epoch_seconds = mktime(&tmv); } // ------------ // 2. 'sscanf' Example [conventional use of 'sscanf'] #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { const char* date = "1998-06-03"; const char* FMT = "%04d-%02d-%02d"; int year, month, day; int result = sscanf(date, FMT, &year, &month, &day); if (result == 0 || result == EOF) { fputs("Date parse error ...\n", stderr); return EXIT_FAILURE; } struct tm tmv = { 0, 0, 0, day, month - 1, year - 1900, -1, -1, -1 }; time_t epoch_seconds = mktime(&tmv); } // ------------ // 3. 'strtok' Example [hardcoded, minimal error checking, assumes date string will not be used // elsewhere, hence may have its contents altered] #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> int main(void) { char date[] = "1998-06-03"; const char* SEP = "-"; int ymd[3]; int i = 0; char* p = strtok(date, SEP); while (p) { if (i > 2) { fputs("Date parse error ...\n", stderr); return EXIT_FAILURE; } ymd[i++] = atoi(p); p = strtok(NULL, SEP); } struct tm tmv = { 0, 0, 0, ymd[2], ymd[1] - 1, ymd[0] - 1900, -1, -1, -1 }; time_t epoch_seconds = mktime(&tmv); } // ------------ // 4. Regex Example [hardcoded, minimal error checking. Assumes date string will not be used // elsewhere, hence may have its contents altered] #include <stdio.h> #include <stdlib.h> #include <time.h> #include <regex.h> int main(void) { char date[] = "1998-06-03"; // Setup regex pattern, and compile; bail out if problem detected const char* PATTERN = "([[:digit:]]{4})-([[:digit:]]{1,2})-([[:digit:]]{1,2})"; regex_t rx; if (regcomp(&rx, PATTERN, REG_EXTENDED) != 0) { fputs("Date parse error [regex compilation failure] ...\n", stderr); return EXIT_FAILURE; } // Hardcoded for 3 [subexpreesion] matches. Match buffer needs to be one larger to // accomodate whole-expression match] const size_t nmatch = 4; regmatch_t match[nmatch]; // Perform regex match int match_result = regexec(&rx, date, nmatch, match, 0); // Regex resources no longer required [only match buffer results required], so free them regfree(&rx); // In the current case, a mismatch indicates an ill-formatted date string was supplied, // so bail out if (match_result != 0) { fputs("Date parse error [regex mismatch] ...\n", stderr); return EXIT_FAILURE; } // NUL-terminate subexpression match areas for easy extraction const char NUL = '\0'; date[match[1].rm_eo] = NUL; date[match[2].rm_eo] = NUL; date[match[3].rm_eo] = NUL; // Convert each subexpression match to required value int year = atoi((char*) date + match[1].rm_so); int month = atoi((char*) date + match[2].rm_so); int day = atoi((char*) date + match[3].rm_so); struct tm tmv = { 0, 0, 0, day, month - 1, year - 1900, -1, -1, -1 }; time_t epoch_seconds = mktime(&tmv); } // ------------ // 5. Raw Pointer Example [hardcoded, no error checking, assumes date string is in the // correct format, and that it will not be used elsewhere, hence may have its contents // altered] #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { char date[] = "1998-06-03"; const char NUL = '\0', SEP = '-'; char *p = date, *q = date; int year, month, day; // Traverse date buffer, NUL-terminating each required substring in turn for easy conversion while (*p++ != SEP) ; *p = NUL; year = atoi(q); q = ++p; while (*p++ != SEP) ; *p = NUL; month = atoi(q); q = ++p; while (*p++ != NUL) ; day = atoi(q); struct tm tmv = { 0, 0, 0, day, month - 1, year - 1900, -1, -1, -1 }; time_t epoch_seconds = mktime(&tmv); } // ---------------------------- #include <stdbool.h> #include <stdio.h> #include <string.h> #include <time.h> struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); struct tm mk_tm_unfilled(void); struct tm mk_tm_zero(void); void show_tm(const struct tm* tmvptr); bool is_parseable_date(const char* date, const char* fmt, struct tm* tmvptr); bool is_leap_year(int year); bool is_valid_hms(int hour, int minute, int second); bool is_valid_ymd(int year, int month, int day); bool is_valid_tm(struct tm* tmvptr); // ---- int main(void) { // Format specification const char* FMT = "%Y/%m/%d"; // Initialise 'struct tm' object struct tm tmv = mk_tm_zero(); // Use a generously-sized input buffer char datebuf[128]; do { // Prompt and get a date string from the user fputs("Enter a date in YYYY/MM/DD form: ", stdout); fflush(stdout); fgets(datebuf, sizeof(datebuf), stdin); // Two stage validation: // * Check that input at least matches 'date' form / structure // * Ensure generated date / time components comprise a 'sensible' value if (is_parseable_date(datebuf, FMT, &tmv) && is_valid_tm(&tmv)) break; // Oops ! fputs("Bad date string - try again\n\n", stdout); fflush(stdout); } while (true); // Let's look at the generated date / time components show_tm(&tmv); } // @@PLEAC@@_3.8 // Printing a date usually sees a broken-down time object either supplied or generated, then // date components extracted and formatted. This may be accomplished: // * Manually; extracting broken-down time components and formatting via a 'printf' family // function // * Via 'strftime' library function, a 'printf'-like function that uses a host of date / time // specific format specifications // // 'ctime' and 'asctime' library functions are available where only a default string // representation is needed. // // In the final part of this section is a self-contained example illustarting, and discussing // in detail, several implementations of a custom function, 'mk_fmt_date', which makes use of // 'strftime' to generate a formatted date string #include <stdio.h> #include <time.h> int main(void) { time_t curtime = time(NULL); // 'ctime' accepts a 'time_t' pointer, and creates a date string in the form: // "Fri May 4 10:38:03 2007\n" printf("%s", ctime(&curtime)); fflush(stdout); } // ------------ #include <stdio.h> #include <time.h> int main(void) { struct tm* curtmptr = ({ time_t curtime = time(NULL); localtime(&curtime); }); // 'asctime' accepts a 'struct tm' pointer and creates a date string in the form: // "Fri May 4 10:38:03 2007\n" printf("%s", asctime(curtmptr)); fflush(stdout); } // ------------ #include <stdio.h> #include <time.h> struct tm mk_tm(int year, int month, int day, int hour, int minute, int second); // ---- int main(void) { struct tm* curtmptr = ({ time_t curtime = time(NULL); localtime(&curtime); }); const char* fmt = "%Y/%m/%d"; // format: YYYY/MM/DD // 'strftime' accepts a 'struct tm' pointer and creates a date string in the form specified // by a format string, one which is similar to that used by the 'printf' function family, // but specialised for date / time formatting. Example below assumes the generated string // will fit within a 32 byte buffer. It is possible to determine the actual buffer size // needed to accomodate the generated string by making an initial call to 'strftime', as // follows: // // #include <limits.h> // ... // int DATESIZE = strftime(NULL, _POSIX_SSIZE_MAX, fmt, curtmptr); // ... // char datebuf[DATESIZE + 1]; // ... // strftime(datebuf, DATESIZE + 1, fmt, curtmptr); // char datebuf[32]; strftime(datebuf, sizeof(datebuf), fmt, curtmptr); printf("%s\n", datebuf); fflush(stdout); // As above, except that a custom helper function is used to create a 'struct tm' object struct tm curtm = mk_tm(2007, 5, 4, 0, 0, 0); strftime(datebuf, sizeof(datebuf), fmt, &curtm); printf("%s\n", datebuf); fflush(stdout); // As above, except that 'printf' is used to perform date string formatting printf("%04d/%02d/%02d\n", curtmptr->tm_year + 1900, curtmptr->tm_mon + 1, curtmptr->tm_mday); fflush(stdout); } // ---------------------------- #include <stdio.h> #include <stdlib.h> #include <time.h> const char* mk_fmt_date(const char* fmt, const struct tm* tmvptr); const char* mk_fmt_date_r(const char* fmt, const struct tm* tmvptr, char* datebuf, size_t bufsize); const char* mk_fmt_date_a(const char* fmt, const struct tm* tmvptr); // ---- int main(void) { struct tm* locptr = ({ time_t curtime = time(NULL); localtime(&curtime); }); // 1. Return pointer to static local storage printf("%s\n", mk_fmt_date("%Y/%m/%d", locptr)); fflush(stdout); // 2. Return pointer to supplied buffer const size_t BUFSIZE = 32; char buffer[BUFSIZE]; printf("%s\n", mk_fmt_date_r("%Y/%m/%d", locptr, buffer, BUFSIZE)); fflush(stdout); // 3. Return dynamically allocated memory pointer // a) Typical use where pointer is captured, used, and finally freed const char* bptr = mk_fmt_date_a("%Y/%m/%d", locptr); printf("%s\n", bptr); fflush(stdout); free(bptr); bptr = NULL; // b) Or, for a slightly more compact solution, use the comma operator const char* bptrf; free(((bptrf = mk_fmt_date_a("%Y/%m/%d", locptr)), printf("%s\n", bptrf), fflush(stdout), bptrf)); bptrf = NULL; // c) Or, for the most compact solution, use a statement expression ({ const char* bptr = mk_fmt_date_a("%Y/%m/%d", locptr); printf("%s\n", bptr); fflush(stdout); free(bptr); }); } // ---- const char* mk_fmt_date(const char* fmt, const struct tm* tmvptr) { // Use of static local storage makes the function self-contained, and keeps the interface // uncluttered; an approach adopted, incidentally, in many of the string-handling library // functions. However, since each invocation of the function updates the same buffer, it is // possible, for example, to inadvertantly use a value generated by a call of this function // in another thread, which is why the use of static local storage is not considered 'thread // safe' [even use of 'buffer locking' code cannot prevent this type of problem] static char datebuf[64]; return (strftime(datebuf, sizeof(datebuf), fmt, tmvptr) == 0) ? NULL : datebuf; } const char* mk_fmt_date_r(const char* fmt, const struct tm* tmvptr, char* datebuf, size_t bufsize) { // The canonical C approach: supply the function a buffer, and associated buffer size // information, and return the buffer's address. This approach clutters the function interface, // as well as making function calls more 'noisy' since storage for the call needs to be // supplied / allocated, and size information obtained. Its advantage, though, is that thread // safety is assured since each call of the function updates a different memory area return (strftime(datebuf, bufsize, fmt, tmvptr) == 0) ? NULL : datebuf; } const char* mk_fmt_date_a(const char* fmt, const struct tm* tmvptr) { // Read-only static local storage is, by definition, thread safe static const size_t MAXBUFSIZE = 256; // Function interface is again uncluttered when using dynamic memory, and the function is // also thread safe. However, the function is no longer self-contained in that the returned // buffer address *must* be captured [usually by assignment to a variable], and the allocated // memory later explicitly deallocated size_t bufsize = strftime(NULL, MAXBUFSIZE, fmt, tmvptr) + 1; char* datebuf = (bufsize == 0) ? NULL : (char*) malloc(bufsize); return (datebuf != NULL && (strftime(datebuf, bufsize, fmt, tmvptr) > 0)) ? datebuf : NULL; } // @@PLEAC@@_3.9 // On single-tasking platforms it can be assumed that elapsed time [i.e. the length of an interval // between two calendar times] equals processor time [i.e the total amount of time a process has // used the CPU]. Not so on multi-tasking platforms where a process relinquishes the CPU [e.g. // the scheduler gives the CPU to another process, process blocks waiting for I/O, or voluntarily // 'goes to sleep']. It is therefore important to be mindful of the difference, and care taken // to use / measure the appropriate one. // // Elapsed time can portably be measured by performing simple arithmetic using 'time_t' values // obtained via the 'time' function. However, the resolution is quite low, being second-based. // Higher resolution timing is possible via platform-specific functionality; in the case of // *NIX / GNU platforms, the 'gettimeofday' function together with the 'timeval' structure, // allows for microsecond-level resolution. // // Should there be a need to measure processor time, the - portable - 'clock_t' type with 'clock' // function is available, while on *NIX / GNU platforms there is 'struct tms' together with // the 'times' function. #include <stdio.h> #include <time.h> int main(void) { time_t start = time(NULL); // ... do work here ... time_t finish = time(NULL); printf("Elapsed time %.9f seconds\n", difftime(finish, start)); fflush(stdout); } // ------------ #include <stdio.h> #include <sys/time.h> int main(void) { struct timeval start, finish; gettimeofday(&start, NULL); // ... do work here ... gettimeofday(&finish, NULL); double elapsed = finish.tv_sec - start.tv_sec + (finish.tv_usec - start.tv_usec) / 1.e6; printf("Elapsed time %.9f seconds\n", elapsed); fflush(stdout); } // ------------ #include <stdio.h> #include <time.h> int main(void) { clock_t start = clock(); // ... do work here ... clock_t finish = clock(); double proctime = ((double) (finish - start)) / CLOCKS_PER_SEC; printf("Processor time %.9f seconds\n", proctime); fflush(stdout); } // ------------ #include <stdio.h> #include <unistd.h> #include <time.h> #include <sys/times.h> int main(void) { const long CLOCK_TICKS = sysconf(_SC_CLK_TCK); struct tms st_cpu, en_cpu; clock_t st_time = times(&st_cpu); // ... do work here ... clock_t en_time = times(&en_cpu); printf("Processor Time (seconds):\n\tReal Time: %.9f, User Time %.9f, System Time %.9f\n", difftime(en_time, st_time) / CLOCK_TICKS, difftime(en_cpu.tms_utime, st_cpu.tms_utime) / CLOCK_TICKS, difftime(en_cpu.tms_stime, st_cpu.tms_stime) / CLOCK_TICKS); fflush(stdout); } // ---------------------------- #include <stdio.h> #include <sys/time.h> int main(void) { struct timeval start, finish; // Start timing gettimeofday(&start, NULL); // ... do work here ... printf("Type in some text, press ENTER when done: "); fflush(stdout); char line[80]; fgets(line, sizeof(line), stdin); // End timing gettimeofday(&finish, NULL); // Compute and print elapsed [not processor] time double elapsed = finish.tv_sec - start.tv_sec + (finish.tv_usec - start.tv_usec) / 1.e6; printf("You took %.9f seconds\n", elapsed); fflush(stdout); } // ------------ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <sys/time.h> void sort(double array[], int size); // ---- int main(void) { struct timeval start, finish; double elapsed = 0.0; const int SIZE = 500, NUMBER = 100; double array[SIZE]; srand(time(NULL)); for (int i = 0; i < NUMBER; ++i) { for (int j = 0; j < SIZE; ++j) array[j] = rand(); gettimeofday(&start, NULL); sort(array, SIZE); gettimeofday(&finish, NULL); elapsed = elapsed + (finish.tv_sec - start.tv_sec + (finish.tv_usec - start.tv_usec) / 1.e6); } // Compute and print average elapsed [not processor] time printf("On average, sorting %d random numbers took %.9f seconds\n", SIZE, elapsed / NUMBER); fflush(stdout); } // ---- void sort(double array[], int size) { int cmp(const void* lp, const void* rp) { const double* lpv = (const double*) lp; const double* rpv = (const double*) rp; return (*lpv > *rpv) - (*lpv < *rpv); } qsort(array, size, sizeof(array[0]), cmp); } // @@PLEAC@@_3.10 // Pausing a process for a designated time period may be accomplished several ways. Only one // approach is portable across platforms, but is a 'busy waiting'-based approach which, while // it allows for sub-second pause periods, has the disadvantage of wasting CPU cycles for the // entire pause period. #include <time.h> void busywait(double seconds); // ---- int main(void) { busywait(0.25); } // ---- void busywait(double seconds) { double elapsed; clock_t start = clock(); do { elapsed = ((double) (clock() - start)) / CLOCKS_PER_SEC; } while (elapsed < seconds); } // ---------------------------- // Approaches where a process is 'put to sleep' i.e. suspended, with its share of the CPU // allocated to other processes, are inherently platform-specific. Under *NIX / GNU, several // library functions may be used: // * Combination of either 'alarm' or 'setitimer', and 'pause', together with appropriate // signal trapping and handling to avoid problems like potential race conditions. Note: // code below is a simplistic, purely illustrative example, and does not do this [need to // use 'sigprocmask', 'sigaction' et al] // * Either 'sleep' [second-resolution] or 'nanosleep' [up to nanosecond-resolution if // supported by the platform]; will terminate before pause period if process receives a // signal // * Either 'select', or 'poll', to implement microsecond-resolution pause not interrupted by // signal #include <signal.h> #include <unistd.h> void alarmSec(long sec); // ---- int main(void) { // Low-resolution: sleep time specified in seconds alarmSec(1); } // ---- void alarmSec(long sec) { static void sig_alrm(int signo) {} if (signal(SIGALRM, sig_alrm) == SIG_ERR) return; alarm(sec); pause(); alarm(0); } // ------------ #include <unistd.h> #include <sys/time.h> void sleepSec(long sec); void sleepMicroSec(long usec); void sleepNanoSec(long nsec); // ---- int main(void) { // Low-resolution: sleep time specified in seconds sleepSec(1); sleep(1); // High-resolution: sleep time specified in microseconds sleepMicroSec(250000); // Very high resolution. However, actual resolution is hardware / platform // dependant, and will be rounded up to level actually supported sleepNanoSec(250000); } // ---- void sleepSec(long sec) { nanosleep(&((const struct timespec) { sec, 0 }), NULL); } void sleepMicroSec(long usec) { nanosleep(&((const struct timespec) { usec / 1000000L, usec % 1000000L * 1000000L }), NULL); } void sleepNanoSec(long nsec) { nanosleep(&((const struct timespec) { 0, nsec }), NULL); } // ------------ #include <unistd.h> #include <sys/time.h> void sleepAbsSec(long sec); void sleepAbsMicroSec(long usec); // ---- int main(void) { // Low-resolution: sleep time specified in seconds sleepAbsSec(1); // High-resolution: sleep time specified in microseconds sleepAbsMicroSec(250000); } // ---- void sleepAbsSec(long sec) { select(0, NULL, NULL, NULL, &((struct timeval) { sec, 0 })); } void sleepAbsMicroSec(long usec) { select(0, NULL, NULL, NULL, &((struct timeval) { usec / 1000000L, usec % 1000000L })); } // @@PLEAC@@_3.11 // @@INCOMPLETE@@ // @@INCOMPLETE@@ // @@PLEAC@@_5.0 //------------------------------------------------------------------ #define _GNU_SOURCE #include <search.h> // hcreate_r() hdestroy_r() struct hsearch_data #include <string.h>// memset() #include <stdio.h>// perror() #include <stdlib.h> //exit() #define TAB 4 ... struct hsearch_data hash; size_t max_element = 42; // of elements in search table ... char * food[] = { "Apple", "Banana", "Lemon", "Carrot" }; char * color[] = { "red", "yellow", "yellow", "orange" }; // we create the hash memset( &hash, 0, sizeof(hash) ); if( hcreate_r(max_element, &hash) == 0 ) { perror("hcreate_r"); exit(1); } /* adding some elements */ // we destroy the hash hdestroy_r( &hash ); ... // @@PLEAC@@_5.1 //------------------------------------------------------------------ ... void hash_add_element(char * elmt_key, char * elmt_data, struct hsearch_data * table) { ENTRY item; ENTRY * ret; item.key = strdup(elmt_key); item.data = strdup(elmt_data); if( hsearch_r(item, ENTER, &ret, table) == 0 ) { perror("hsearch_r"); exit(1); } return; } ... ENTRY entry; ENTRY * found; int i; /* hash creation */ hash_add_element(food[0], color[0], &hash); hash_add_element(food[1], color[1], &hash); hash_add_element(food[2], color[2], &hash); hash_add_element(food[3], color[3], &hash); fprintf(stdout, "Known foods:\n"); for( i = 0; i < TAB; i++ ) { entry.key = food[i]; hsearch_r( entry, FIND, &found, &hash ); fprintf(stdout, "%s\n", found->key); // It is the responsibility of the program code // to free the elements contained in the hashing table free(found->key); free(found->data); } /* hash destruction */ ... # Known foods: # Apple # Banana # Lemon # Carrot // @@PLEAC@@_5.2 //------------------------------------------------------------------ ... entry.key = "Martini"; if( hsearch_r( entry, FIND, &found, &hash ) != 0 ) // Martini exists else // it doesn't ... // @@PLEAC@@_5.3 //------------------------------------------------------------------ // we can't delete an element with the hashing table // provided by the GNU C library // @@PLEAC@@_7.4 //------------------------------------------------------------------ #include <stdio.h> #include <string.h> // strerror() #include <errno.h> // errno ... FILE * fp; if( (fp = fopen(filename, mode)) == NULL ) fprintf( stderr, "Couldn't open %s for reading : %s\n", filename, strerror(errno) ); ... // @@PLEAC@@_7.5 //------------------------------------------------------------------ #include <stdio.h> // tmpfile() ... FILE * file; // /!\ the temporary filename isn't accessible file = tmpfile(); ... //------------------------------------------------------------------ #include <stdio.h> // remove() #include <stdlib.h> // mkstemp() ... int fd; char template[20]; strcpy( template, "tmp/XXXXXX" ); fd = mkstemp( template ); if( fd < 0 ) { perror( mkstemp ); exit( 1 ); } remove( template ); // now go on to use the file ... ... //------------------------------------------------------------------ #include <stdio.h> // tempnam() #include <stdlib.h> #include <unistd.h> // unlink() #include <fcntl.h> // open() #include <sys/stat.h> #include <sys/types.h> ... char * tmpname = NULL; int fd; while(1) { tmpname = tempnam( NULL, NULL ); if( tmpname == NULL ) { perror( "tempname" ); exit(1); } // is another process creating a temporary file at the same moment? // we don't know. // => we have to ensure an exclusive open fd = open( tmpname, O_CREAT | O_EXCL | O_RDWR, 0600 ); // temporary file will be destroy on close() call unlink( tmpname ); free( tmpname ); if( fd < 0 ) perror( "open" ); else break; } // now go on to use the file ... ... // @@PLEAC@@_8.2 //------------------------------------------------------------------ /* using external program : wc -l */ #include <stdio.h> #include <stdlib.h> // system(), exit(); malloc(), free() #include <string.h> // strlen() #include <errno.h> // errno ... char file[100] = argv[1]; // a pathname char * command; // we format the shell command command = malloc( (strlen("wc -l ") + 1) + (strlen(file) + 1) ); sprintf(command, "%s %s", "wc -l", argv[1]); // bad solution system(command); // good solution : using popen() instead of system() FILE * message; if( (message = popen(command, "r")) == NULL ) { fprintf(stderr, "error popen %d\n", errno); exit(1); } char wc_output[100]; fgets(wc_output, sizeof wc_output, message); fprintf(stdout, "%s", wc_output); pclose(message); free(command); ... //------------------------------------------------------------------ /* using standard C library */ #include <stdio.h> ... char file[100] = argv[1]; // a pathname FILE * fp; if( (fp = fopen( file, "r" )) == NULL ) { perror("fopen"); exit(1); } int line = 0; char ch; while ( (ch = getc(fp)) != EOF ) if (ch == '\n') line++; fprintf(stdout, "number of lines : %d\n", line ); fclose(fp); ... //------------------------------------------------------------------ /* using system calls /!\ fast implementation, the whole file is in memory */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> // open() #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> // lseek(), close() ... char file[100] = argv[1]; // a pathname int fd; fd = open(file, O_RDONLY); size_t size; size = lseek(fd, 0, SEEK_END ); // size of the buffer char * buffer; buffer = malloc(size); lseek(fd, 0, SEEK_SET); // back to the beginning of the file if( (read(fd, buffer, size)) != size ) { perror("read"); exit(1); } close(fd); int line = 0; int i; for( i = 0; i < size; i++ ) { if(buffer[i] == '\n') line++; } free(buffer); fprintf(stdout, "number of lines : %d\n", line ); ... // @@PLEAC@@_9.0 //------------------------------------------------------------------ #include <stdio.h> // perror(), fprintf() #include <unistd.h> // stat(), fstat(), lstat() #include <sys/stat.h> // struct stat ... struct stat entry; if( stat("/usr/bin/vi", &entry) < 0 ) perror(""); if( S_ISREG(entry.st_mode) ) fprintf( stderr, "file\n" ); ... //------------------------------------------------------------------ #include <stdio.h> // perror(), fprintf() #include <unistd.h> // stat(), fstat(), lstat() #include <sys/stat.h> // struct stat ... struct stat entry; if( stat("/usr/bin", &entry) < 0 ) perror(""); if( S_ISDIR( entry.st_mode) ) fprintf( stderr, "directory\n" ); ... //------------------------------------------------------------------ // we can't access the inode of a file with a stream as parameter // we need to pass a file descriptor parameter to fstat #include <stdio.h> // perror(), fprintf() #include <unistd.h> // stat(), fstat(), lstat() #include <sys/stat.h> // struct stat #include <fcntl.h> //open() ... struct stat entry; int fd; // file descriptor FILE * fp; // stream if((fd = open("/etc/group", O_RDONLY)) < 0) perror(""); if( fstat(fd, &entry) < 0 ) perror(""); if((fp = fdopen(fd, "r" )) == NULL ) // we attach the file descriptor to a stream perror(""); ... //------------------------------------------------------------------ #include <stdio.h> // perror() #include <unistd.h> // stat(), fstat(), lstat() #include <sys/stat.h> // struct stat #include <sys/types.h> // off_t ... struct stat entry; time_t ctime; off_t size; if( stat("/usr/bin/vi", &entry) < 0 ) perror(""); ctime = entry.st_ctime; size = entry.st_size; //fprintf( stdout, "ctime: %ld\nsize: %ld\n", ctime, size ); ... //------------------------------------------------------------------ // No equivalent in C ANSI/POSIX/GNU for -T (file is a text file)? /* # -s for filesize (test if nonzero) unless (-s F && -T _) { die "$filename doesn't have text in it.\n"; } */ //------------------------------------------------------------------ #include <stdio.h> #include <dirent.h> // struct dirent, opendir(), readdir(), closedir() #include <sys/types.h> // DIR type ... DIR * dir; struct dirent * entry; dir = opendir("/usr/bin"); if( dir == NULL ) perror(""); while((entry = readdir(dir)) != NULL) fprintf(stdout, "Inside /usr/bin is something called %s\n", entry->d_name); fprintf(stdout, "\n"); closedir(dir); ... // @@PLEAC@@_9.10 //------------------------------------------------------------------ // we use the XPG version of basename which overrides // the GNU version (not portable) if libgen.h is included // /!\ do not call basename or dirname with a static string (bugs in glibc) #include <stdio.h> // fprintf() #include <stdlib.h> // free() #include <string.h> // strdup(), strrchr() #include <libgen.h> // basename(), dirname() ... char * dir, * file, * ext; char * tmp_dirname, * tmp_file; char * path = "/usr/lib/libc.a"; tmp_dirname = strdup(path); tmp_file = strdup(path); dir = dirname(tmp_dirname); file = basename(tmp_file); ext = strrchr(path, '.'); // extension as everything after the last '.' fprintf(stdout, "dir is %s, file is %s, ext is %s\n", dir, file, ext); // dir is /usr/lib, file is libc.a, ext is .a free(tmp_file); free(tmp_dirname); ... //------------------------------------------------------------------ #include <stdio.h> // fprintf() #include <stdlib.h> // free() #include <string.h> // strdup() #include <libgen.h> // basename(), dirname() ... char * file, * dir, * tmp_file, * tmp_dirname; char * path = "/usr/lib/libc.a"; tmp_file = strdup( path ); tmp_dirname = strdup( path ); dir = dirname( tmp_dirname ); file = basename( tmp_file ); fprintf( stdout, "dir is %s, file is %s\n", dir, file ); // dir is /usr/lib, file is libc.a free(tmp_dirname); free(tmp_file); ... //------------------------------------------------------------------ // no equivalent in C for fileparse() but we can // implement a (very basic) similar function #include <stdio.h> // fprintf() #include <stdlib.h> // free() #include <string.h> // strdup(), strrchr(), strlen() typedef struct Pathname_ { char * dirname; char * filename; char * extension; }Pathname; void fileparse( const char * path, Pathname * split_path) { char * tmp_path, * ptr, * extension; tmp_path = strdup(path); ptr= strrchr( tmp_path, '/'); *ptr = '\0'; ptr++; // dirname split_path->dirname = strdup(tmp_path); // filename split_path->filename = malloc(strlen(ptr) + 1); strcpy(split_path->filename, ptr); // extension extension = strrchr(path, '.'); // extension as everything after the last '.' split_path->extension = strdup(extension); free(tmp_path); return; } int main( int argc, char * argv[] ) { Pathname fparsed; char * path = "/usr/lib/libc.a"; fileparse(path, &fparsed); fprintf(stdout, "dir is %s, file is %s, ext is %s\n", fparsed.dirname, fparsed.filename, fparsed.extension); // dir is /usr/lib, file is libc.a, ext is .a free(fparsed.dirname); free(fparsed.filename); free(fparsed.extension); return 0; } //------------------------------------------------------------------ // we are Unix centric so no equivalent for fileparse_set_fstype("MacOS"); //------------------------------------------------------------------ char * extension(const char * path){ char * ext; ext = strrchr(path, '.'); if(ext == NULL) return NULL; return ext; } // fprintf(stdout, "ext is %s\n", extension(path)); // ext is .a // @@PLEAC@@_10.0 // NOTE: In order to keep the size / verbosity of code examples to a minimum, library function // calls will generally not include error checking and handling. Example: // // p = malloc(...); if ((p = malloc(...)) == NULL) // { // ... handle error ... // } // // However, any necessary, application-specific error-related code, will still be used. Code // examples will endeavour, wherever possible, to make use of: // // * C99 Features e.g. variable-length arrays, non-const aggregate initialisers // * GNU Extensions e.g. nested functions, statement expressions // // The aim of doing so is to reduce redundancy [i.e. copious examples of older / standard C // already exist] as well as enhance the information value of each PLEAC example. // // Another item worthy of note is the use of writeable 'static local storage' in many custom // functions. Whilst a commonly-used technique that makes functions self-contained, and easier // to use [which is precisely why it is used here], it is not viable in multi-threaded code; // examples need to be suitably modified to work in such code. The section, 'Printing a Date', // in Chapter 3: Dates and Times, discusses this issue, and provides illustrative examples. #include <stdio.h> // Declaring the variable as 'static' ensures it has 'file scope', that is: // * It may be considered globally accessable within the current source file // * Is not visible to code defined outside the current file static int greeted = 0; int howManyGreetings(void); void hello(void); // ------------ int main(void) { hello(); int greetings = howManyGreetings(); printf("bye there!, there have been %d greetings so far\n", greetings); } // ------------ int howManyGreetings(void) { return greeted; } void hello(void) { printf("high there!, this function has been called %d times\n", ++greeted); } // @@PLEAC@@_10.1 // Standard C requires that a function be prototyped, hence the name and type of parameters // must be specified, and the argumemt list in any calls to that function must match the // parameter list, as illustrated here. #include <math.h> double hypotenuse(double side1, double side2); // ---- int main(void) { double diag = hypotenuse(3.0, 4.0); } // ---- double hypotenuse(double side1, double side2) { return sqrt(pow(side1, 2.0) + pow(side2, 2.0)); } // ---------------------------- // Standard C does not support the dynamic packaging / unpackaging of arguments, thus it is // *not* possible to call a function which has, for example, been declared to expect two // parameters, with a single argument, to to pass it its arguments packed as elements in // a single array and expect those elements to be 'unpacked' from the array. If the function // is to be passed an array when called, then it must be declared to expect an array. Put // simply, the function call must conform to the function declaration and definition #include <math.h> double hypotenuse(double sidearr[]); // ---- int main(void) { double sidearr[] = {3.0, 4.0}; double diag = hypotenuse(sidearr); } // ---- double hypotenuse(double sidearr[]) { return sqrt(pow(sidearr[0], 2.0) + pow(sidearr[1], 2.0)); } // ---------------------------- // Scope does exist to implement functions that may be passed a variable number of parameters. // However, such functions have to be especially written to determine the number and type of // arguments passed, a task that is generally quite precarious because there is little system // support for it, and it is strongly reliant on the programmer adhering to certain conventions. // For instance, it is not possible to determine the type of the arguments passed. Instead, this // must be determined by 'hints' [e.g. 'printf' uses the format specifiers in the format // string to determine argument type], or by strictly adhering to other conventions [e.g. // assuming a specific number of arguments, or using a value to indicate the 'last' argument #include <math.h> #include <stdarg.h> // Note: at least one parameter must be specified, the rest may then be unspecified i.e. a // variable number double hypotenuse(double side1, ...); // ---- int main(void) { double diag = hypotenuse(3.0, 4.0); } // ---- // Note: at least one parameter must be specified, the rest may then be unspecified i.e. a // variable number double hypotenuse(double side1, ...) { va_list ap; va_start(ap, side1); // Here we're assuming exactly two arguments are passed: the first argument is 'side1' // and the first [and only] variable argument is extracted into 'side2'. Any additional // arguments ar simply ignored. Normally, however, 'va_arg' is placed in a loop, and // each argument extracted in turn double side2 = va_arg(ap, double); va_end(ap); return sqrt(pow(side1, 2.0) + pow(side2, 2.0)); } // ---------------------------- #include <stddef.h> void int_all(const double arr[], int retarr[], size_t arrsize); // ---- int main(void) { const double nums[] = {1.4, 3.5, 6.7}; const size_t ARRSIZE = sizeof(nums) / sizeof(double); int ints[ARRSIZE]; int_all(nums, ints, ARRSIZE); } // ---- void int_all(const double arr[], int retarr[], size_t arrsize) { for (size_t i = 0; i < arrsize; ++i) { // Since 'retarr' is type 'int', implicit data conversion occurs, but data could // be lost; 'arr' is untouched, and is protected since it is 'const' qualified retarr[i] = arr[i]; } } // ---------------------------- #include <stddef.h> #include <math.h> void trunc_em(double arr[], size_t arrsize); // ---- int main(void) { double nums[] = {1.4, 3.5, 6.7}; trunc_em(nums, sizeof(nums) / sizeof(double)); } // ---- void trunc_em(double arr[], size_t arrsize) { for (size_t i = 0; i < arrsize; ++i) { // Safer to use 'floor' instead of casting arr[i] = floor(arr[i]); } } // @@PLEAC@@_10.2 // Variables declared within a function body are local to that function, and those declared // outside a function body are global, that is, are visible throughout the executable // unless their visibility has been restricted to the source file in which they are defined // via the 'static' keyword void somefunc(void) { // All these variables are local to this function int variable; int another, an_array[5]; ; // ... } // ---------------------------- #include <stddef.h> #include <stdlib.h> #include <string.h> // File scope variables static char* name = NULL; static int age = 0; static int c = 0; static int condition = 0; void run_check(void); void check_x(int x); // ---- int main(int argc, char** argv) { name = strcpy(malloc(strlen(argv[1]) + 1), argv[1]); age = atoi(argv[2]); check_x(age); free(name); } // ------------ void run_check(void) { // Full access to file scope variables condition = 1; // ... } void check_x(int x) { // Full access to file scope variables const char y[] = "whatever"; run_check(); // 'condition' updated by 'run_check' if (condition) { ; // ... } } // @@PLEAC@@_10.3 // Through use of the 'static' keyword it is possible to create 'persistent private variables', // that is, variables that are accessable only by a select set of functions, and that retain // their value in between function calls. In Standard C these may be implemented in two ways: // * File Scope Variables. Here a source file contains global variable(s) [those residing // outside any function body] declared as 'static'. Only the set of function defined within // that file has access those variables, thus they may be considered 'private', and since // they retain their value in between function calls, are also 'persistent' // File: 'mysubs.h' void mysub(void); void reset(void); // ---- // File: 'mysubs.c' static int variable = 1; void mysub(void) { ; // ... do something with 'variable' ... } void reset(void) { variable = 1; } // ---- // File: 'test.c' #include "mysubs.h" int main(void) { // 'variable' is not accessable here // Call 'mysub', which can access 'variable' mysub(); // Call 'reset' which sets 'variable' to 1 reset(); } // ------------ // File: 'counter.h' int increment(void); int decrement(void); // File: 'counter.c' static int counter = 0; int increment(void) { return ++counter; } int decrement(void) { return --counter; } // File: 'test.c' #include <stdio.h> #include "counter.h" int main(void) { int a = increment(); printf("%d\n", a); a = decrement(); printf("%d\n", a); } // * Function Scope Variables. These are local variables declared 'static'; they are visible // only within the function body [hence are 'private'], and persist in between calls of that // function #include <stdio.h> enum CMD_TYPE {INC_CMD, DEC_CMD}; int Counter(enum CMD_TYPE cmd_type); // ---- int main(void) { int a = Counter(INC_CMD); printf("%d\n", a); a = Counter(DEC_CMD); printf("%d\n", a); } // ---- int Counter(enum CMD_TYPE cmd_type) { static int counter = 0; // GNU Extension: nested functions, have direct access to 'counter' int increment(void) { return ++counter; } int decrement(void) { return --counter; } if (cmd_type == INC_CMD) increment(); if (cmd_type == DEC_CMD) decrement(); return counter; } // @@PLEAC@@_10.4 // Standard C offers no facility for performing ad-hoc, runtime stack inspection; therefore, // information such as the currently-executing function name, cannot be obtained. However, // there is a GNU extension which allows the embedding of the function name within the // function body, and code may be written to somehow utilise this information. Two additional // GNU extension functions - '__builtin_return_address' and '__builtin_frame_address' - *do* // allow runtime stack inspection. However, this does not include access to information such // as the function name. void whoami(void) { // A Standard C facility, '__func__', performs a similar role printf("I am function: %s\n", __FUNCTION__); } // @@PLEAC@@_10.5 // Standard C supports only 'pass-by-value', that is, a copy of each argument is passed when // calling a function. The approach is the same whether the argument is a primitive type such // as an 'int', an aggregate type such as a struct, or an array, or, a specialised type like // a pointer. Despite this, 'pass-by-reference' is possible, though it is performed indirectly // via pointers. Put simply, when an argument needs to be modified, or to avoid the overhead // of copying a potentially 'large' argument, its address [i.e. pointer] is passed in its // place. The pointer is then used to refer to the actual item [hence 'reference']. // In all such cases, bar one, the address of the referred item must be specifically obtained. // The exception is when passing arrays; the system automatically passes a pointer rather than // copying the array: the address of the first array element from which all other element // locations can be computed. void array_diff(int arr1[], int arr2[]); // ---- int main(void) { int arr1[] = {1, 2, 3}, arr2[] = {4, 5, 6}; // Call 'array_diff' with 'arr1' and 'arr2' as arguments. Although each argument is // passed-by-value, because they are arrays, only the address of the first element // is passed. Effectively, 'references' to these arrays are passed, and the overhead // of copying is avoided array_diff(arr1, arr2); } // ---- void array_diff(int arr1[], int arr2[]) { ; // ... } // ---------------------------- #include <stdlib.h> int* add_vecpair(const int* vec1, const int* vec2, int size); // ---- int main(void) { int a[] = {1, 2}, b[] = {5, 8}; int* c = add_vecpair(a, b, 2); free(c); } // ---- int* add_vecpair(const int* vec1, const int* vec2, int size) { int* retvec = malloc(size * sizeof(int)); for(int i = 0; i < size; ++i) { retvec[i] = vec1[i] + vec2[i]; } return retvec; } // @@PLEAC@@_10.6 // Standard C is a statically-typed language based on type declarations. This means that: // * Each variable, as well each function's parameters and return value, is declared to have // a type; this is a permanent attribute which cannot be altered [except through 'casting' // which may be seen as selective circumvention of the type system]. Put simply, a variable // declared to be of type 'X' can only be assigned such types; a function declared to return // type 'X' must return such a type // * Type checking is done at compilation time, so it should not be possible [except through // casting] to generate code that is not type conformant e.g. attempts to assign type 'Y' // where a type 'X' is expected, or to return an array instead of a struct from a function, // should fail as compilation errors // // The short of it is that, in Standard C, 'return context' is something that is determined at // compilation time, therefore is not something that can be altered. Runtime-determined // 'return context' is really only something meaningful in dynamically-typed, interpreted // languages. // // That being said, it *is* possible to implement a crude, very limited, form of // runtime-determined 'return context' via the use of 'void*'. The Perl 'mysub' example, below, // will use this approach. Please note the intent here is to show possibilities; the approach is // an example of 'selective type system circumvention', and is not generally a recommended one // unless it is part of a larger library specifcally designed for this purpose #include <stdio.h> enum ret_type { WANT_NULL, WANT_INT, WANT_INT_ARRAY } RET_TYPE; void* mysub(void* arg); // ---- int main(void) { mysub(NULL); RET_TYPE = WANT_INT; int a = mysub(5); printf("%d\n", a); RET_TYPE = WANT_INT_ARRAY; int arr[] = {1, 2, 3}; int* retarr = mysub(arr); printf("%d:%d:%d\n", arr[0], arr[1], arr[2]); } // ---- void* mysub(void* arg) { if (RET_TYPE == WANT_INT) return (int) arg; if (RET_TYPE == WANT_INT_ARRAY) return (int*) arg; return NULL; } // @@PLEAC@@_10.7 // Standard C offers no support for named / keyword parameters. It is of course possible to // mimic such functionality in several ways: // * Adopt a convention of passing arguments as hash table entries, or as list nodes, as // an array of entries // * Bury key=value pairs in a string, and pass string as single argument // * Use variable argument functions with structs of key / value pairs [or, as a variation // on the second suggestion, key=value strings] // // In all cases argument unpacking must be performed within the function body, so the // approach can hardly be called transparent. Additionally, a fair amount of code would be // needed to build a usable, flexible and robust facility. Examples of each approach are // shown. // 1. Array of struct named_parm_t [For simplicity, 'value' is assumed to be a 'char*', // but could instead use a 'type tagging' approach [i.e. a field identifying type of // data being stored, and either a void* or a union, for storing the various types] #include <stdio.h> #include <stdlib.h> #include <string.h> struct named_parm_t { char* key; char* value; }; void named_parms_as_array_of_struct(const struct named_parm_t parms[], size_t num_parms); // ---- int main(void) { // a=1, b=2, c=cat named_parms_as_array_of_struct((struct named_parm_t[]){{"a", "1"}, {"b", "2"}, {"c", "cat"}}, 3); // b=12 [a and c have default values] named_parms_as_array_of_struct((struct named_parm_t[]){{"b", "12"}}, 1); } // ---- void named_parms_as_array_of_struct(const struct named_parm_t parms[], size_t num_parms) { // Ensure local variables are initialised to sensible default values int a = 0, b = 0; const char* c = "default"; // Extract named parameter values, and assign to corresponding local variable(s) for (int i = 0; i < num_parms; ++i) { if (strcmp(parms[i].key, "a") == 0) { a = atoi(parms[i].value); continue; } if (strcmp(parms[i].key, "b") == 0) { b = atoi(parms[i].value); continue; } if (strcmp(parms[i].key, "c") == 0) c = parms[i].value; } printf("Value of a is %d, b is %d, and c is %s\n", a, b, c); fflush(stdout); } // ------------ // 2. Delimited string of key=value substrings #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> void named_parms_as_string(const char* parms); // ---- int main(void) { // "a=1,b=2,c=cat" named_parms_as_string("a=1,b=2,c=cat"); // "b=12" [a and c have default values] named_parms_as_string("b=2"); } // ---- void named_parms_as_string(const char* parms) { const char COMMA = ',', EQ = '=', NUL = '\0'; bool parse_entry(char* entry, const char sep, const char** key, const char** value) { char *p = entry, *q; return ((q = strchr(p, EQ)) != NULL) ? ({ *key = p; *q = NUL; *value = ++q; true; }) : false; } // Ensure local variables are initialised to sensible default values int a = 0, b = 0; const char* c = "default"; // Parse string of key=value entries ... char entry[64]; const char *key, *value; const char *p = parms, *q; while (p != NULL) { if ((q = strchr(p, COMMA)) == NULL) q = strchr(p, NUL); memcpy(entry, p, q - p); *(entry + (q - p)) = NUL; // Parse each entry, assign to corresponding variable if (parse_entry(entry, EQ, &key, &value)) { if (strcmp(key, "a") == 0) a = atoi(value); else if (strcmp(key, "b") == 0) b = atoi(value); else if (strcmp(key, "c") == 0) c = value; } p = (*q) ? ++q : NULL; } printf("Value of a is %d, b is %d, and c is %s\n", a, b, c); fflush(stdout); } // ---------------------------- // 3. Variable arguments of key=value strings // // The example from Perl Cookbook is implemented using this approach . Note that the code // here does not perform exactly the same task as the original. Instead, emphasis is placed // on illustrating string parsing and argument handling techniques #include <stdbool.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <ctype.h> #include <string.h> void the_func(const char* arg1, ...); // ---- int main(void) { the_func("increment=20s", "start=+5m", "finish=+30m", NULL); the_func("start=+5m", "finish=+30m", NULL); the_func("finish=+30m", NULL); the_func("start=+5m", "increment=15s", NULL); } // ---- void the_func(const char* arg1, ...) { const char COMMA = ',', EQ = '=', NUL = '\0'; bool parse_entry(char* entry, const char sep, const char** key, const char** value) { char *p = entry, *q; return ((q = strchr(p, EQ)) != NULL) ? ({ *key = p; *q = NUL; *value = ++q; true; }) : false; } int inc_secs(char* inc_tok) { char* lp = strchr(inc_tok, NUL) - 1; char inc_mul = tolower(*lp); *lp = NUL; if (inc_mul == 's') return atoi(inc_tok); if (inc_mul == 'm') return atoi(inc_tok) * 60; return 0; } // Ensure local variables are initialised to sensible default values int finish = 0, start = 0; char increment[32] = "10s"; // Storage for parsing ... char entry[64]; const char *key, *value; // Extract values from 1st argument if (parse_entry(strcpy(entry, arg1), EQ, &key, &value)) { if (strcmp(key, "start") == 0) start = inc_secs(value); else if (strcmp(key, "finish") == 0) finish = inc_secs(value); else if (strcmp(key, "increment") == 0) strcpy(increment, value); } // Setup for variable argument handling, and extract values from each of these const char* arg; va_list ap; va_start(ap, arg1); while ((arg = va_arg(ap, const char*)) != NULL) { // Parse each entry, assign to corresponding variable if (parse_entry(strcpy(entry, arg), EQ, &key, &value)) { if (strcmp(key, "start") == 0) start = inc_secs(value); else if (strcmp(key, "finish") == 0) finish = inc_secs(value); else if (strcmp(key, "increment") == 0) strcpy(increment, value); } } va_end(ap); printf("start -> %d : finish -> %d : increment -> %s\n", start, finish, increment); fflush(stdout); } // @@PLEAC@@_10.8 // It's generally only interpreted languages, or those supporting some sort of pattern // matching that implement such a facility. In Standard C a function must return a value in // conformance with its return type specification [returning nothing - 'void' - may still be // seen as returning a value]. Where the return value is an aggregate - struct or array - it // is not possible to perform selective 'masking out' of it's contents; the entire item is // captured then processed as needs be. Therefore the examples in this section could not be // implemented // @@PLEAC@@_10.9 // Standard C allows only the return of a single value. The return of multiple values *can*, // however, be simulated by packaging them within an aggregate type: a struct or an array. // The catch is, however, that the return value must be manually 'unpacked', thus using // this approach is a deliberate design decision rather than the use of an ad-hoc facility #include <stdlib.h> #include <stdio.h> typedef struct HASH_ { char* key; char* value; } HASH; typedef struct ARRAY_HASH_ { char* array; HASH* hash; } ARRAY_HASH; ARRAY_HASH some_func(char array[], HASH hash[]); // ---- int main(void) { // GNU Extensions: compound literals ARRAY_HASH refs = some_func((char[]) {'a', 'b', 'c'}, (HASH[]) {{"k1", "v1"}, {"k2", "v2"}}); printf("%c\n", refs.array[1]); printf("%s:%s\n", refs.hash[1].key, refs.hash[1].value); } // ---- ARRAY_HASH some_func(char array[], HASH hash[]) { ; // ... do something with 'array' and 'hash' // GNU Extensions: compound literals return (ARRAY_HASH) {array, hash}; } // @@PLEAC@@_10.10 // Unlike in Perl, it is not possible for a function to selectively return / not return a // value; a function is defined to either return a specific type, or to return no value at // all [i.e. to return 'void']. Thus, this approach cannot be used to 'return failure'. // Conventions used in Standard C to indicate function failure include: // * Return a pointer; the return of a NULL-valued pointer may indicate 'failure' such as, // for example, the inability to allocate dynamic memory, or, in the case of the 'fopen' // library function, the failure to open a file // * Return an 'int' value where a 0 or positive value indicates success, and a -1 value // indicates failure. Some library functions also set the 'errno' global variable to a // known error code for further diagnostic information #include <stdlib.h> char* func(void) { int error_detected = 0; char* valid_char_pointer; ; // ... if (error_detected) return NULL; ; // ... return valid_char_pointer; } // ---------------------------- #include <stdlib.h> #include <stdio.h> void die(const char* msg); int sfunc(void); char* afunc(void); char* hfunc(void); // ------------ int main(void) { int s; char *a, *h; if ((s = sfunc()) == -1) die("'sfunc' failed\n"); if ((a = afunc()) == NULL) die("'afunc' failed\n"); if ((h = hfunc()) == NULL) die("'hfunc' failed\n"); } // ------------ void die(const char* msg) { fputs(msg, stderr); exit(EXIT_FAILURE); } // ---- int sfunc(void) { int error_detected = 0; int valid_int_value; ; // ... if (error_detected) return -1; ; // ... return valid_int_value; } char* afunc(void) { int error_detected = 0; char* valid_char_pointer; ; // ... if (error_detected) return NULL; ; // ... return valid_char_pointer; } char* hfunc(void) { int error_detected = 0; char* valid_char_pointer; ; // ... if (error_detected) return NULL; ; // ... return valid_char_pointer; } // @@PLEAC@@_10.11 // Whilst in Perl function prototyping is optional, this is not the case in C, where it is // necessary to: // * Declare a function before use; this could either be a function declaration separate from // the function definition, or the function definition itself which serves as its own // declaration // * Specify both parameter positional and type information; parameter names are optional in // declarations, mandatory in definitions // * Specify return type; in the past this was optional, but now this is mandatory #include <stdlib.h> #include <stdio.h> // Function Declaration int* myfunc(int arg1, int arg2); // Also possible: int* myfunc(int, int); // ---- int main(void) { // Call function with all required arguments; this is the only calling method // [except for calling via function pointer which still needs all arguments supplied] int* results = myfunc(3, 5); // Let's look at our return array's contents printf("%d:%d\n", results[0], results[1]); free(results); } // ---- // Function Definition int* myfunc(int arg1, int arg2) { // Allocate some memory, pack arguments into array for return int* results = malloc(2 * sizeof(int)); *(results + 0) = arg1; *(results + 1) = arg2; return results; } // ------------ // Other Perl examples are omitted since there is no variation in C function calling or // parameter handling // @@PLEAC@@_10.12 // Though perhaps not a widely used idiom in Standard C the language does offer exception // handling support. Some pertinent reading: // // * http://en.wikipedia.org/wiki/Exception_handling // * http://portal.acm.org/citation.cfm?coll=GUIDE&dl=GUIDE&id=512997 // // The example in this section doesn't strctly follow the Perl code, but merely shows how // an exception can be thrown, and handled. The approach used is based on code from: // // * http://ldeniau.home.cern.ch/ldeniau/html/exception/exception.html // // It is worth mentioning that 'exception handling support' consists of the provision, in // the Standard C library, of the 'setjmp' and 'longjmp' functions. Exception handling // code such as that used here is essentially an infrastrucure built around these two // functions with 'setjmp' being used to create a destination for a later invocation of // 'longjmp' to 'goto' [a.k.a. perform a non-local exit], and in the process, ensuring // the 'program state' is wound back to the time the 'setjmp' call was made. Further // details are obtainable by referencing the relevant documentation. #include <stdio.h> #include "exception.h" enum { E_invalid = 0, // This exception can never be thrown E_bad_exit, // 'standard' exceptions E_bad_alloc, E_bad_cast, E_bad_typeid, E_usr_fullmoon, // User-defined exceptions E_last_exception // Last exception id }; int main(void) { printf("main - entry\n"); try { printf("try block - entry\n"); printf("... doing stuff ...\n"); // if (... error condition detected ...) throw(E_usr_fullmoon); // Control never gets here ... printf("try block - end\n"); } catch (E_usr_fullmoon) { printf("Caught a 'fullmoon' exception\n"); } catch_any { printf("Caught an unknown exception, error code: %d\n", exception); } endtry; // Control gets here regardless of whether an exception is thrown or not printf("main - end\n"); } // Include here, or separately compile and link with your executable #include "exception.c" // @@PLEAC@@_10.13 // In Standard C it *isn't possible* to access a global variable that has been shadowed by a // local variable of the same name, thus there is no need to save / restore such variables. If // both global and local variable need to be accessable, consider renaming one of them. An // oft used convention is to name global variables using capitals; thus the same name may be // used to indicate the variables are related, but the different case ensures that each remains // visible #include <stdio.h> // Global variable static int age = 18; // ---- void print_age(void) { // Global value, 'age', is accessed printf("Age is %d\n", age); } // ------------ int main(void) { // A local variable named, 'age' will act to 'shadow' the globally // defined version, thus any changes to, 'age', will not affect // the global version int age = 5; // Prints 18, the current value of the global version print_age(); // Local version is altered, *not* global version age = 23; // Prints 18, the current value of the global version print_age(); } // ---------------------------- #include <stdio.h> // Global variable static int age = 18; // ---- void print_age(void) { // Global value, 'age', is accessed printf("Age is %d\n", age); } // ------------ int main(void) { // Here no local version declared: any changes affect global version age = 5; // Prints 5, the new value of the global version print_age(); // Global version again altered age = 23; // Prints 23, the new value of the global version print_age(); } // ---------------------------- #include <stdio.h> // Global variable int AGE = 18; // ---- void print_age(void) { // Global value, 'AGE', is accessed printf("Age is %d\n", AGE); } // ------------ int main(void) { int age = AGE; // Prints 18, the new value of 'AGE' print_age(); // Global variable is altered AGE = 23; // Prints 23, the new value of 'AGE' print_age(); // 'AGE' restored from saved local version AGE = age; // Prints 18, the restored value of 'AGE' print_age(); } // @@PLEAC@@_10.14 // Standard C does not allow the redefinition of a function at runtime. A function's name // [as seen in source code] is, at runtime, actually an unalterable value: the address of // a chunk of code. Therefore, a function named 'f' will, for as long as the executable // exists, be associated with a particular chunk of code. void f(void) { ; // ... } // ---- int main(void) { f(); // Call function 'f' void (*fp)(void) = f; // Get address of function 'f'; place in function pointer variable fp(); // Call function 'f' using its address } // However, if the convention of using a function pointer variable to call a function, is // adopted, then it becomes possible to call one of possibly several same-signature [i.e. // same parameter list and return type] functions by simply assigning a different address // to the variable. In this way it is possible to not only execute other available functions, // but also those residing in external libraries [i.e. shared libraries / DLL's] // Perl 'grow / expand' combined example #include <stdio.h> typedef void (*FPTR)(void); void grow(void) { printf("grow\n"); } void expand(void) { printf("expand\n"); } // ---- int main(void) { // Display addresses of each function; each should be a different value printf("grow -> %x | expand -> %x\n", grow, expand); // Call functions directly by name grow(); expand(); // Call functions indirectly using function pointer variable FPTR fp = grow; fp(); fp = expand; fp(); // Local scope { // Current version of 'fp' will shadow outer scope version FPTR fp = grow; // Should now be 'grow' fp(); } // Should still be 'expand' since the 'fp' at this scope was untouched fp(); } // ---------------------------- #include <stdio.h> typedef void (*FPTR)(void); void fred_func(void) { printf("fred\n"); } void barney_func(void) { printf("barney\n"); } // ---- int main(void) { // Note: 'fred' is the pointer variable name. It is *not* possible to redefine function // names FPTR fred = fred_func; fred(); fred = barney_func; fred(); } // ---------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> const char* red(const char* text) { return strcat(strcat(strcpy(malloc(64), "<FONT COLOR='red'>"), text), "</FONT>"); } // ---- int main(void) { const char* color_tag = red("careful here"); printf("%s\n", color_tag); free(color_tag); } // ---------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> const char* color_font(const char* color, const char* text) { return strcat(strcat(strcat(strcat(strcpy(malloc(128), "<FONT COLOR='"), color), "'>"), text), "</FONT>"); } const char* red(const char* text) { return color_font("red", text); } const char* blue(const char* text) { return color_font("blue", text); } const char* green(const char* text) { return color_font("green", text); } // ---- int main(void) { const char* color_tag = red("careful here"); printf("%s\n", color_tag); free(color_tag); color_tag = blue("careful here"); printf("%s\n", color_tag); free(color_tag); color_tag = green("careful here"); printf("%s\n", color_tag); free(color_tag); } // ---------------------------- // It isn't possible, in Standard C, to generate code 'on the fly' as is done in the Perl // examples. The code must have been pre-generated, and either statically linked [i.e. // actually part of the executable] or be dynamically loadable [i.e. residing in an external // library which may be located and loaded]. A more appropriate way to solve this problem // is simply to directly call the 'color_font' function with the required arguments, two // approaches of which are shown below in (1) and (2) #include <stdio.h> #include <stdlib.h> #include <string.h> const char* color_font(const char* color, const char* text) { return strcat(strcat(strcat(strcat(strcpy(malloc(128), "<FONT COLOR='"), color), "'>"), text), "</FONT>"); } // ---- // (1) Traverse parallel arrays of arguments int main(void) { const char* colors[] = {"red", "blue", "green", "yellow", "orange", "purple", "violet", NULL}; const char* texts[] = {"x", "y", "z", "a", "b", "c", "d", NULL}; const char* color_tag; for (const char **color = colors, **text = texts; *color != NULL; ++color, ++text) { color_tag = color_font(*color, *text); printf("%s\n", color_tag); free(color_tag); } } // ---- // (2) Package arguments as a struct; traverse array of such structs typedef struct TAG_TEXT_ { const char* color; const char* text; } TAG_TEXT; int main(void) { const TAG_TEXT tags[] = { {"red", "x"}, {"blue", "y"}, {"green", "z"}, {"yellow", "a"}, {"orange", "b"}, {"purple", "c"}, {"violet", "d"} }; const int TAGS = sizeof(tags) / sizeof(TAG_TEXT); const char* color_tag; for (int i = 0; i < TAGS; ++i) { color_tag = color_font(tags[i].color, tags[i].text); printf("%s\n", color_tag); free(color_tag); } } // @@PLEAC@@_10.15 // Undefined function calls are detected as errors at code generation time: // // * At compilation time a function must be explicitly declared before being called i.e. it // must have a prototype, else a compilation error occurs // * At linkage time [i.e. when currently-generated code is combined with library code to // create an executable module] the code identified with a function must be available, // [or, with shared libraries, in a known location] else a linkage error occurs // // In short, in the Standard C language, no runtime detection of 'missing' functions is // possible since such code could never have been generated in the first place. // // However, if the convention of using a function pointer to call a function, is adopted, // it does become possible to check whether the function pointer variable is assigned a non-NULL // address, presumably the address of a valid function. Unfortunately, without the aid of // specialised, platform-specific library routines, it is not possible to verify the validity // of this address; an attempted function call either succeeds or fails, and may fail because // it was a bogus address. Again, the Standard C language offers no runtime detection of // something like a 'bogus' function address. // // The combination of 'call-by-function-pointer', and a suitable set of shared library handling // functions does make it possible to implement an dynamic code loading facility, even a // full-blown AUTOLOAD facility. However, it is important to realise that this is an // infrastructure built on top of the Standard C language, not an integral language feature. // // In the *NIX environment the 'dlopen', 'dlsymbol', 'dlerror', and 'dlclose' set of functions // is commonly used for shared library management, and would form the basis for implementing // an AUTOLOAD facility. The following is an example of a very minimal dynamic loading system // illustrating how a function named 'chartreuse' residing in a shared library called 'colors.so' // would be loaded, called, and unloaded. Note that this is not an example of dynamic code // generation, as in the Perl example, since the 'chartreuse' function *must already exist* // for it to even have a chance of executing. // // Of course it is possible to write code that creates C source file(s), invokes various // code generation tools, and builds a shared library which may subsequently be loaded and // run. Again, though, this is a custom infrastructure, not an integral language facility. // @@INCOMPLETE@@ // @@PLEAC@@_10.16 //------------------------------------------------------------------ // Nested functions are supported by GNU C as an extension // See the documentation of your GCC version int outer( int arg1 ){ int x = arg1 + 35; int inner() { return x * 19; } // nested function can access // all the variables of the containing function // that are visible at the point of its definition return x + inner(); } // @@PLEAC@@_10.17 // @@INCOMPLETE@@ // @@INCOMPLETE@@ // @@PLEAC@@_11.15 //------------------------------------------------------------------ /* binary tree demo program */ #define _GNU_SOURCE // getline(), tdestroy() #include <stdio.h> #include <search.h> // tsearch(), tfind(), tdelete(), tdestroy(); twalk() #include <stdlib.h> // rand(), malloc(), free() static VISIT walk_type; int compar_int( const void * elm1, const void * elm2) { return ( *((int *) elm1) - *((int *) elm2) ); } void walk( const void * node, const VISIT method, const int level) { int * data; data = *(int **)node; if(method == walk_type) fprintf( stdout, "%d ", *data ); if( method == leaf ) fprintf( stdout, "%d ", *data ); return; } void free_elmt( void * node ){ free( node ); return; } int main( void ) { void * root = NULL; int i, *ptr; // first generate 20 random inserts for( i = 0; i < 20; i++ ) { ptr = malloc(sizeof(int)); *ptr = (int)( 1000 * (double)rand() / (RAND_MAX + 1.0) ); if( tsearch( ptr, &root, compar_int ) == NULL ) { perror("tsearch"); exit(1); } } // now dump out the tree all three ways fprintf(stdout, "Pre order\n"); walk_type = preorder; twalk( root, walk ); printf("\n"); fprintf(stdout, "In order\n"); walk_type = postorder; // /!\ postorder is rather confusing twalk( root, walk ); printf("\n"); fprintf(stdout, "Post order\n"); walk_type = endorder; twalk( root, walk ); printf("\n"); // prompt until EOF char * istr; size_t size; ssize_t ret; while(1) { fprintf(stdout, "Search? : "); size = 0; istr = NULL; ret = getline( &istr, &size, stdin ); if( ret == -1 ) break; sscanf( istr, "%d", &i ); free(istr); if( tfind( &i, &root, compar_int ) == NULL ) fprintf( stdout, "No %d in tree\n", i ); else fprintf( stdout, "Found %d\n", i ); } tdestroy( root, free_elmt ); return 0; } // @@PLEAC@@_15.10 //------------------------------------------------------------------ // /!\ this function is deprecated #include <unistd.h> // getpass() ... char * password = getpass( "password : " ); ... //------------------------------------------------------------------ // gcc -Wall -o checkuser checkuser.c -lcrypt #define _GNU_SOURCE #include <stdio.h> // gnu version of getline() #include <stdlib.h> // at_exit() #include <string.h> #include <unistd.h> // getlogin() #include <termios.h> // struct termios, tcgetattr(), tcsetattr() #include <pwd.h> // struct passwd, fgetpwent() #include <crypt.h> // crypt() static struct termios stored_settings; void restore_term_setting( void ) { /* restore terminal setting */ tcsetattr( 0, TCSANOW, &stored_settings); } int main( void ) { struct termios new_settings; char * cryptpw; // our encrypted password char * my_login = getlogin(); // our login /* we ask for password */ fprintf( stdout, "Password : " ); /* we disable echo input */ // we first set an exit handler to restore terminal setting // * if something go wrong // * on the final return if( atexit( restore_term_setting ) != 0 ) { fprintf( stderr, "error on atexit\n" ); exit(1); } tcgetattr( 0, &stored_settings ); // save terminal setting new_settings = stored_settings; new_settings.c_lflag &= (~ECHO); // disable echo input characters tcsetattr( 0, TCSANOW, &new_settings ); /* we get the plain password */ char * passwd = NULL; size_t size = 0; ssize_t ret; if( (ret = getline( &passwd, &size, stdin )) == -1 ) exit(1); // we "chomp" the plain password char *p = strchr( passwd, '\n' ); *p = 0; /* we get our login encrypted password /!\ most of the Linux system use shadow password we have to use /etc/shadow instead of /etc/passwd /!\ we have to be root in order to read /etc/shadow */ FILE * fp; if( (fp = fopen( "/etc/shadow", "r" )) == NULL ) { perror("fopen"); exit(1); } struct passwd * shadow_pwd_ent; cryptpw = NULL; while( (shadow_pwd_ent = fgetpwent(fp)) != NULL ) { // we search our login in the entry returned if( strcmp( my_login, shadow_pwd_ent->pw_name ) == 0 ) { cryptpw = shadow_pwd_ent->pw_passwd; break; } } fclose(fp); /* password matching */ int match = strcmp( crypt(passwd, cryptpw), cryptpw ); // we hurry to free the plain version of the password free(passwd), passwd = NULL; puts( "" ); if( match == 0 ) printf( "Welcome, %s\n", my_login ); else printf( "You are not %s\n", my_login ); return 0; } // @@PLEAC@@_17.8 //------------------------------------------------------------------ #include <stdio.h> #include <unistd.h> // gethostname() #include <stdlib.h> // malloc(), realloc(), free() #include <errno.h> // errno ... char * hostname = NULL; size_t sizebuf = 8; hostname = malloc(size); while( gethostname(hostname, sizebuf ) != 0 ) { if( errno != ENAMETOOLONG ) { perror("gethostname"); exit(1); } // the buffer size is too small sizebuf += 8; hostname = realloc(hostname, sizebuf); } printf("my hostname : %s\n", hostname); free(hostname); ... //------------------------------------------------------------------ #include <stdio.h> #include <sys/utsname.h> // struct utsname, uname() ... struct utsname my_utsname; uname( &my_utsname ); printf("my hostname : %s\n", my_utsname.nodename); ... //------------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> #include <netdb.h> // gethostbyname(), gethostbyaddr(), h_errno, herror() #include <netinet/in.h> // struct in_addr #include <arpa/inet.h> // inet_ntoa() ... // we don't handle IPV6 for more simplicity struct in_addr address; struct in_addr * ip; struct hostent * host; int i; /* gethostbyname() */ char hostname[] = "a_valid_hostname"; if( (host = gethostbyname(hostname)) == NULL ) { herror("gethostbyname"); exit(1); } for( i = 0; host->h_addr_list[i] != NULL; i++ ) { ip = ( struct in_addr *)(host->h_addr_list[i]); printf("address for %s : %s\n", hostname ,inet_ntoa(*ip)); } /* gethostbyaddr() */ char addr[] = "127.0.0.1"; // we verify the IPV4 address if( inet_aton(addr, &address) == 0 ) { fprintf(stderr, "invalid address\n"); return 1; } if( (host = gethostbyaddr( (char *)&address, sizeof(struct in_addr), AF_INET )) == NULL ) { herror("gethostbyaddr"); exit(1); } printf("hostname for %s : %s\n", addr, host->h_name ); ...