/* * Copyright (C) by Argonne National Laboratory * See COPYRIGHT in top-level directory */ #include "mpl.h" #include #ifdef MPL_HAVE_UNISTD_H #include #endif #ifdef MPL_HAVE_SYS_TYPES_H #include #endif #ifdef MPL_HAVE_SYS_PARAM_H #include #endif #ifdef MPL_HAVE_TIME_H #include #endif #if !defined MPL_HAVE_SNPRINTF int MPL_snprintf(char *str, size_t size, const char *format, ...) { int n; const char *p; char *out_str = str; va_list list; va_start(list, format); p = format; while (*p && size > 0) { char *nf; nf = strchr(p, '%'); if (!nf) { /* No more format characters */ while (size-- > 0 && *p) { *out_str++ = *p++; } } else { int nc; int width = -1; /* Copy until nf */ while (p < nf && size-- > 0) { *out_str++ = *p++; } /* p now points at nf */ /* Handle the format character */ nc = nf[1]; if (isdigit(nc)) { /* Get the field width */ /* FIXME : Assumes ASCII */ width = nc - '0'; p = nf + 2; while (*p && isdigit(*p)) { width = 10 * width + (*p++ - '0'); } /* When there is no longer a digit, get the format * character */ nc = *p++; } else { /* Skip over the format string */ p += 2; } switch (nc) { case '%': *out_str++ = '%'; size--; break; case 'd': { int val; char tmp[20]; char *t = tmp; /* Get the argument, of integer type */ val = va_arg(list, int); sprintf(tmp, "%d", val); if (width > 0) { size_t tmplen = strlen(tmp); /* If a width was specified, pad with spaces on the * left (on the right if %-3d given; not implemented yet */ while (size-- > 0 && width-- > tmplen) *out_str++ = ' '; } while (size-- > 0 && *t) { *out_str++ = *t++; } } break; case 'x': { int val; char tmp[20]; char *t = tmp; /* Get the argument, of integer type */ val = va_arg(list, int); sprintf(tmp, "%x", val); if (width > 0) { size_t tmplen = strlen(tmp); /* If a width was specified, pad with spaces on the * left (on the right if %-3d given; not implemented yet */ while (size-- > 0 && width-- > tmplen) *out_str++ = ' '; } while (size-- > 0 && *t) { *out_str++ = *t++; } } break; case 'p': { void *val; char tmp[20]; char *t = tmp; /* Get the argument, of pointer type */ val = va_arg(list, void *); sprintf(tmp, "%p", val); if (width > 0) { size_t tmplen = strlen(tmp); /* If a width was specified, pad with spaces on the * left (on the right if %-3d given; not implemented yet */ while (size-- > 0 && width-- > tmplen) *out_str++ = ' '; } while (size-- > 0 && *t) { *out_str++ = *t++; } } break; case 's': { char *s_arg; /* Get the argument, of pointer to char type */ s_arg = va_arg(list, char *); while (size-- > 0 && s_arg && *s_arg) { *out_str++ = *s_arg++; } } break; default: /* Error, unknown case */ return -1; break; } } } va_end(list); if (size-- > 0) *out_str++ = '\0'; n = (int) (out_str - str); return n; } #endif /* MPL_HAVE_SNPRINTF */ /*@ MPL_strdup - Duplicate a string Synopsis: .vb char *MPL_strdup(const char *str) .ve Input Parameters: . str - null-terminated string to duplicate Return value: A pointer to a copy of the string, including the terminating null. A null pointer is returned on error, such as out-of-memory. Module: Utility @*/ #if !defined MPL_HAVE_STRDUP char *MPL_strdup(const char *str) { char *restrict p = (char *) malloc(strlen(str) + 1); const char *restrict in_p = str; char *save_p; save_p = p; if (p) { while (*in_p) { *p++ = *in_p++; } *p = 0; } return save_p; } #endif /* MPL_HAVE_STRDUP */ /* * MPL_strncpy - Copy at most n characters. Stop once a null is reached. * * This is different from strncpy, which null pads so that exactly * n characters are copied. The strncpy behavior is correct for many * applications because it guarantees that the string has no uninitialized * data. * * If n characters are copied without reaching a null, return an error. * Otherwise, return 0. * * Question: should we provide a way to request the length of the string, * since we know it? */ /*@ MPL_strncpy - Copy a string with a maximum length Input Parameters: + instr - String to copy - maxlen - Maximum total length of 'outstr' Output Parameters: . outstr - String to copy into Notes: This routine is the routine that you wish 'strncpy' was. In copying 'instr' to 'outstr', it stops when either the end of 'outstr' (the null character) is seen or the maximum length 'maxlen' is reached. Unlike 'strncpy', it does not add enough nulls to 'outstr' after copying 'instr' in order to move precisely 'maxlen' characters. Thus, this routine may be used anywhere 'strcpy' is used, without any performance cost related to large values of 'maxlen'. If there is insufficient space in the destination, the destination is still null-terminated, to avoid potential failures in routines that neglect to check the error code return from this routine. Module: Utility @*/ int MPL_strncpy(char *dest, const char *src, size_t n) { char *restrict d_ptr = dest; const char *restrict s_ptr = src; register int i; if (n == 0) return 0; i = (int) n; while (*s_ptr && i-- > 0) { *d_ptr++ = *s_ptr++; } if (i > 0) { *d_ptr = 0; return 0; } else { /* Force a null at the end of the string (gives better safety * in case the user fails to check the error code) */ dest[n - 1] = 0; /* We may want to force an error message here, at least in the * debugging version */ /*printf("failure in copying %s with length %d\n", src, n); */ return 1; } } /* replacement for strsep. Conforms to the following description (from the OS X * 10.6 man page): * * The strsep() function locates, in the string referenced by *stringp, the first occur- * rence of any character in the string delim (or the terminating `\0' character) and * replaces it with a `\0'. The location of the next character after the delimiter * character (or NULL, if the end of the string was reached) is stored in *stringp. The * original value of *stringp is returned. * * An ``empty'' field (i.e., a character in the string delim occurs as the first charac- * ter of *stringp) can be detected by comparing the location referenced by the returned * pointer to `\0'. * * If *stringp is initially NULL, strsep() returns NULL. */ char *MPL_strsep(char **stringp, const char *delim) { int i, j; char *ret; if (!*stringp) return NULL; ret = *stringp; i = 0; while (1) { if (!ret[i]) { *stringp = NULL; return ret; } for (j = 0; delim[j] != '\0'; ++j) { if (ret[i] == delim[j]) { ret[i] = '\0'; *stringp = &ret[i + 1]; return ret; } } ++i; } } /* there's no standard portable way to convert error codes to human readable * strings. The standard way to do that is via strerror() but if for some * resason we don't have it, then we'll merely output the error code seen */ #if !defined MPL_HAVE_STRERROR char *MPL_strerror(int errnum) { #define STRERROR_SIZE 256 static char msgbuf[STRERROR_SIZE]; snprintf(msgbuf, STRERROR_SIZE, "errno = %d", errnum); #undef STRERROR_SIZE return msgbuf; } #endif /* MPL_HAVE_STRERROR */ /*@ MPL_strnapp - Append to a string with a maximum length Input Parameters: + instr - String to copy - maxlen - Maximum total length of 'outstr' Output Parameters: . outstr - String to copy into Notes: This routine is similar to 'strncat' except that the 'maxlen' argument is the maximum total length of 'outstr', rather than the maximum number of characters to move from 'instr'. Thus, this routine is easier to use when the declared size of 'instr' is known. Module: Utility @*/ int MPL_strnapp(char *dest, const char *src, size_t n) { char *restrict d_ptr = dest; const char *restrict s_ptr = src; register int i; /* Get to the end of dest */ i = (int) n; while (i-- > 0 && *d_ptr) d_ptr++; if (i <= 0) return 1; /* Append. d_ptr points at first null and i is remaining space. */ while (*s_ptr && i-- > 0) { *d_ptr++ = *s_ptr++; } /* We allow i >= (not just >) here because the first while decrements * i by one more than there are characters, leaving room for the null */ if (i >= 0) { *d_ptr = 0; return 0; } else { /* Force the null at the end */ *--d_ptr = 0; /* We may want to force an error message here, at least in the * debugging version */ return 1; } } static unsigned int xorshift_rand(void) { /* time returns long; keep the lower and most significant 32 bits */ unsigned int val = time(NULL) & 0xffffffff; /* Marsaglia's xorshift random number generator */ val ^= val << 13; val ^= val >> 17; val ^= val << 5; return val; } /*@ MPL_create_pathname - Generate a random pathname Input Parameters: + dirname - String containing the path of the parent dir (current dir if NULL) + prefix - String containing the prefix of the generated name - is_dir - Boolean to tell if the path should be treated as a directory Output Parameters: . dest_filename - String to copy the generated path name Notes: dest_filename should point to a preallocated buffer of PATH_MAX size. Module: Utility @*/ void MPL_create_pathname(char *dest_filename, const char *dirname, const char *prefix, const int is_dir) { /* Generate a random number which doesn't interfere with user application */ const unsigned int rdm = xorshift_rand(); const unsigned int pid = (unsigned int) getpid(); if (dirname) { MPL_snprintf(dest_filename, PATH_MAX, "%s/%s.%u.%u%c", dirname, prefix, rdm, pid, is_dir ? '/' : '\0'); } else { MPL_snprintf(dest_filename, PATH_MAX, "%s.%u.%u%c", prefix, rdm, pid, is_dir ? '/' : '\0'); } }