// -*- 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 );
  
...