/***************************************************************************** * myutil.c * * DESCRIPTION * This file contains some simple utility functions. * * HISTORY * 12/2002 Arthur Taylor (MDL / RSIS): Created. * * NOTES ****************************************************************************/ #include #include #include #include #include #include #include "libaat.h" #ifdef MEMWATCH #include "memwatch.h" #endif /***************************************************************************** * reallocFGets() -- Arthur Taylor / MDL * * PURPOSE * Read in data from file until a \n is read. Reallocate memory as needed. * Similar to fgets, except we don't know ahead of time that the line is a * specific length. * Assumes that S is either NULL, or points to Len memory. Responsibility * of caller to free the memory. * * ARGUMENTS * S = The string of size Size to store data in. (Input/Output) * Size = The allocated length of S. (Input/Output) * fp = Input file stream (Input) * * RETURNS: int * -1 = Memory allocation error. * 0 = we read only EOF * strlen (*S) (0 = Read only EOF, 1 = Read "\nEOF" or "EOF") * * HISTORY * 12/2002 Arthur Taylor (MDL/RSIS): Created. * 2/2007 AAT (MDL): Updated. * * NOTES * 1) Based on getline (see K&R C book (2nd edition) p 29) and on the * behavior of Tcl's gets routine. * 2) Choose STEPSIZE = 80 because pages are usually 80 columns. ****************************************************************************/ #define STEPSIZE 80 int reallocFGets(char **S, size_t *Size, FILE *fp) { char *str = *S; /* Local copy of string. */ int c; /* Current char read from stream. */ int i; /* Where to store c. */ myAssert(sizeof(char) == 1); for (i = 0; ((c = getc(fp)) != EOF) && (c != '\n'); ++i) { if (i >= *Size) { if ((str = (char *)realloc((void *)*S, *Size + STEPSIZE)) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } *S = str; *Size = *Size + STEPSIZE; } str[i] = (char)c; } if (c == '\n') { /* Make room for \n\0. */ if (*Size < i + 2) { if ((str = (char *)realloc((void *)*S, i + 2)) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } *S = str; *Size = i + 2; } str[i] = (char)c; ++i; } else { /* Make room for \0. */ if (*Size < i + 1) { if ((str = (char *)realloc((void *)*S, i + 1)) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } *S = str; *Size = i + 1; } } str[i] = '\0'; return i; } #undef STEPSIZE /***************************************************************************** * strncpyTrim() -- Arthur Taylor / MDL * * PURPOSE * Perform a strncpy, but only copy the non-white space. It looks at the * first n bytes of src, and removes white space from left and right sides, * copying the result to dst. * Unlike strncpy, it doesn't fill with '\0'. * Also, it adds a '\0' to end of dst, so it assumes dst is allocated to at * least (n+1). * * ARGUMENTS * dst = The resulting string (Output) * src = The string to copy/trim (Input) * n = The number of bytes to copy/trim (Input/Output) * * RETURNS: char * * returns "dst" * * HISTORY * 3/2007 Arthur Taylor (MDL/RSIS): Created. * * NOTES ****************************************************************************/ char *strncpyTrim(char *dst, const char *src, size_t n) { const char *ptr; /* Pointer to where first non-white space is. */ const char *ptr2; /* Pointer to last non-white space. */ if (src == NULL) { *dst = '\0'; return dst; } for (ptr = src; (isspace(*ptr) && (ptr < src + n)); ++ptr) { } /* Did we hit the end of an all space string? */ if ((*ptr == '\0') || (ptr == src + n)) { *dst = '\0'; return dst; } for (ptr2 = src + n - 1; isspace(*ptr2); --ptr2) { } strncpy(dst, ptr, ptr2 - ptr + 1); dst[ptr2 - ptr + 1] = '\0'; return dst; } #ifdef OLD /***************************************************************************** * mySplit() -- Arthur Taylor / MDL * * PURPOSE * Split a character array according to a given symbol. Responsibility of * caller to free the memory. * Assumes that argc is either 0, or is the number of entries allocated in * argv. * * ARGUMENTS * data = character string to look through. (Input) * symbol = character to split based on. (Input) * argc = number of groupings found. (Input/Output) * argv = characters in each grouping. (Input/Output) * f_trim = Should trim the white space from each element in list? (Input) * * RETURNS: int * -1 = Memory allocation error. * 0 = Ok * * HISTORY * 5/2004 Arthur Taylor (MDL/RSIS): Created. * 3/2007 AAT (MDL): Updated. * * NOTES ****************************************************************************/ int mySplit(const char *data, char symbol, size_t *Argc, char ***Argv, char f_trim) { const char *head; /* The head of the current string */ const char *ptr; /* a pointer to walk over the data. */ size_t argc; /* number of symbols in data + 1 */ char **argv; /* Local copy of Argv */ size_t len; /* length of current string. */ size_t i; /* loop counter over Argc */ /* Free data from previous call. */ for (i = 0; i < *Argc; i++) { free((*Argv)[i]); } /* Count number of breaks */ argc = 0; head = data; while (head != NULL) { ptr = strchr(head, symbol); if (ptr != NULL) { head = ptr + 1; /* The following is in case data is not '\0' terminated */ if ((head != NULL) && (*head == '\0')) { head = NULL; } } else { head = NULL; } ++argc; } /* Allocate memory once */ if (*Argc == 0) { if ((argv = (char **)malloc(argc * sizeof(char *))) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } } else if (*Argc != argc) { argv = (char **)realloc((void *)(*Argv), argc * sizeof(char *)); if (argv == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } } else { argv = *Argv; } /* Store memory */ head = data; i = 0; while (head != NULL) { ptr = strchr(head, symbol); if (ptr != NULL) { len = ptr - head; if ((argv[i] = (char *)malloc(len + 1)) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } if (f_trim) { strncpyTrim(argv[i], head, len); } else { strncpy(argv[i], head, len); argv[i][len] = '\0'; } head = ptr + 1; /* The following is in case data is not '\0' terminated */ if ((head != NULL) && (*head == '\0')) { head = NULL; } } else { /* Handle from here to end of text. */ len = strlen(head); if ((argv[i] = (char *)malloc(len + 1)) == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } if (f_trim) { strncpyTrim(argv[i], head, len); } else { strcpy(argv[i], head); } head = NULL; } ++i; } /* Set results */ *Argc = argc; *Argv = argv; return 0; } #endif /***************************************************************************** * mySplit() -- Arthur Taylor / MDL * * PURPOSE * Split a character array according to a given symbol. Responsibility of * caller to free the memory (see ASSUMPTIONS). * The original code copied from data to a 2 dimmensional list. This is * slower than it needs to be since repeated calls would have to free the * 2d list and allocated a new one, resulting in lots of allocs and frees. * The new code mimics the reallocFgets idea by using spData. spData is * of size lenSpData, and is large enough to hold the user's 1d 'data' array. * It will increase to meet demand. * The Argv data can now point to the spData memory so one doesn't have to * repeatedly free / alloc the memory. Improvements for a simple project went * from 14 sec to 4 sec run time, with a massive reduction (2,217,893 -> 3) in * the number of alloc requests. * * ASSUMPTIONS * 1) argc = 0 (argv = NULL), or is the number of entries allocated in argv. * 2) lenSpData = 0 (spData = NULL), or is the allocated length for spData. * 3) User free's the data via: free(Argv); free(spData); * * ARGUMENTS * data = character string to look through. (Input) * symbol = character to split based on. (Input) * lenSpData = allocated length of spData (Input/Output) * spData = copy of data with symbol replaced with \0 and trimmed (In/Out) * Argc = number of list elements. (Input/Output) * Argv = pointers into spData for start of each list element. (In/Out) * f_trim = Should trim the white space from each element in list? (Input) * * RETURNS: int * -1 = Memory allocation error. * 0 = Ok * * HISTORY * 5/2004 Arthur Taylor (MDL/RSIS): Created. * 3/2007 AAT (MDL): Updated. * 11/2007 AAT (MDL): Updated. * 5/2008 AAT: Bug fix. If buffer ends in a then it doesn't count * that symbol in argc, but then tries to store the '\0' in the argv * array, causing a segfault. Chose to count the symbol. So if argc * is always 1 more than the number of symbols in buffer. * * NOTES * Example upgrade from old call... * Old: * size_t numCol = 0; * char **col = NULL; * if (mySplit(buffer, ',', &numCol, &col, 1) != 0) { * goto error; * } * if (numCol != 0) { * for (i = 0; i < numCol; ++i) { * free(col[i]); * } * free(col); * } * New * size_t spBuffLen = 0; * char *spBuff = NULL; * size_t numCol = 0; * char **col = NULL; * if (mySplit(buffer, ',', &spBuffLen, &spBuff, &numCol, &col, 1) != 0) { * goto error; * } * if (spBuff != NULL) { * free(spBuff); * } * if (col != NULL) { * free(col); * } ****************************************************************************/ int mySplit(const char *data, char symbol, size_t *lenSpData, char **SpData, size_t *Argc, char ***Argv, char f_trim) { size_t dataLen; /* String length of data */ const char *head; /* The head of the current string */ const char *ptr; /* a pointer to walk over the data. */ size_t argc; /* number of symbols in data + 1 */ size_t cnt; /* Current list element we are working on */ char **argv; /* Local copy of Argv */ size_t i; /* loop counter over Argc */ char *spData; /* Local copy of SpData */ /* Count number of breaks */ argc = 0; head = data; while (head != NULL) { ptr = strchr(head, symbol); if (ptr != NULL) { head = ptr + 1; /* The following is in case data is not '\0' terminated */ if ((head != NULL) && (*head == '\0')) { head = NULL; ++argc; } } else { head = NULL; } ++argc; } /* Allocate memory for Argv */ if (*Argc != argc) { /* Try to protect *Argv from bad realloc */ argv = (char **)realloc((void *)(*Argv), argc * sizeof(char *)); if (argv == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } /* Good realloc, so set *Argv */ *Argv = argv; *Argc = argc; } else { argv = *Argv; } /* Allocated memory for spData. */ dataLen = strlen(data); if (*lenSpData < dataLen + 1) { /* Try to protect *SpData from bad realloc */ spData = (char *)realloc((void *)(*SpData), (dataLen + 1) * sizeof(char)); if (spData == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } /* Good realloc, so set *SpData */ *SpData = spData; *lenSpData = dataLen + 1; } else { spData = *SpData; } /* Update argv by pointing to spData, and copy data to spData. */ cnt = 0; argv[cnt] = spData; for (i = 0; i < dataLen; ++i) { if (data[i] == symbol) { spData[i] = '\0'; if (f_trim) { strTrim (argv[cnt]); } ++cnt; argv[cnt] = spData + (i + 1); } else { spData[i] = data[i]; } } spData[dataLen] = '\0'; if (f_trim) { strTrim (argv[cnt]); } return 0; } /***************************************************************************** * myAtoI() -- Arthur Taylor / MDL * * PURPOSE * Returns true if all char are digits except a leading + or -, or a * trailing ','. Ignores leading or trailing white space. Value is set to * atoi(s). * * ARGUMENTS * s = character string to look at. (Input) * value = the converted value of 's', if 's' is a number. (Output) * * RETURNS: int * 0 = Not an integer * 1 = Integer * * HISTORY * 2/2007 Arthur Taylor (MDL): Commented * * NOTES ****************************************************************************/ int myAtoI(const char *s, sInt4 *value) { char *extra = NULL; /* The data after the end of the integer. */ *value = 0; myAssert(s != NULL); if (s == NULL) { return 0; } while (*s != '\0') { if (isdigit(*s) || (*s == '+') || (*s == '-')) { *value = strtol(s, &extra, 10); if (errno == ERANGE) { return 0; } myAssert(extra != NULL); if (*extra == '\0') { return 1; } /* First trailing char should be space or ',' */ if ((!isspace(*extra)) && (*extra != ',')) { *value = 0; return 0; } ++extra; /* The rest should all be white space. */ while (*extra != '\0') { if (!isspace(*extra)) { *value = 0; return 0; } ++extra; } return 1; } else if (!isspace(*s)) { return 0; } ++s; } return 0; } /***************************************************************************** * myAtoI_Len() -- Arthur Taylor / MDL * * PURPOSE * Returns true if "len" char are digits except a leading + or -, or a * trailing ','. Ignores leading or trailing white space. Value is set to * atoi(s). * * ARGUMENTS * s = character string to look at. (Input) * len = number of characters to pay attention to. (Input) * value = the converted value of 's', if 's' is a number. (Output) * * RETURNS: int * 0 = Not an integer * 1 = Integer * * HISTORY * 4/2007 Arthur Taylor (MDL): Created * * NOTES * Tried to modify myAtoI for efficiency sake, but ran into problems since * strtol doesn't have a "str_n_tol" version. So I would have had to pass in * a "char *s" instead of a "const char *s". Figured I'd do the quick way * with calls to myAtoI, and re-visit to see if I could get a const char * implementation. ****************************************************************************/ int myAtoI_Len(char *s, size_t len, sInt4 *value) { char c_temp; /* Value which is temporarily replaced with a \0 */ int ierr; /* Error code (aka is 's' a float or not?) */ if (strlen(s) <= len) { return myAtoI(s, value); } else { c_temp = s[len]; s[len] = '\0'; ierr = myAtoI(s, value); s[len] = c_temp; return ierr; } } /***************************************************************************** * myAtoF() -- Arthur Taylor / MDL * * PURPOSE * Returns true if all char are digits except a leading + or -, or a * trailing ',' and up to one '.'. Ignores leading or trailing white space. * Value is set to atof(s). * * ARGUMENTS * s = character string to look at. (Input) * value = the converted value of 's', if 's' is a number. (Output) * * RETURNS: int * 0 = Not a real number, * 1 = Real number. * * HISTORY * 7/2004 Arthur Taylor (MDL): Updated * 4/2005 AAT (MDL): Did a code walk through. * 2/2007 Arthur Taylor (MDL): Commented * * NOTES * Used to be myIsReal() ****************************************************************************/ int myAtoF(const char *s, double *value) { char *extra; /* The data after the end of the double. */ *value = 0; myAssert(s != NULL); if (s == NULL) { return 0; } while (*s != '\0') { if (isdigit(*s) || (*s == '+') || (*s == '-') || (*s == '.')) { *value = strtod(s, &extra); if (errno == ERANGE) { return 0; } myAssert(extra != NULL); if (*extra == '\0') { return 1; } /* Allow first trailing char for ',' */ if ((!isspace(*extra)) && (*extra != ',')) { *value = 0; return 0; } ++extra; /* Make sure the rest is all white space. */ while (*extra != '\0') { if (!isspace(*extra)) { *value = 0; return 0; } ++extra; } return 1; } else if (!isspace(*s)) { return 0; } ++s; } return 0; } /***************************************************************************** * myAtoF_Len() -- Arthur Taylor / MDL * * PURPOSE * Returns true if "len" char are digits except a leading + or -, or a * trailing ',' and up to one '.'. Ignores leading or trailing white space. * Value is set to atof(s). * * ARGUMENTS * s = character string to look at. (Input) * len = number of characters to pay attention to. (Input) * value = the converted value of 's', if 's' is a number. (Output) * * RETURNS: int * 0 = Not a real number, * 1 = Real number. * * HISTORY * 4/2007 Arthur Taylor (MDL): Created * * NOTES * Tried to modify myAtoF for efficiency sake, but ran into problems since * strtod doesn't have a "str_n_tod" version. So I would have had to pass in * a "char *s" instead of a "const char *s". Figured I'd do the quick way * with calls to myAtoF, and re-visit to see if I could get a const char * implementation. ****************************************************************************/ int myAtoF_Len(char *s, size_t len, double *value) { char c_temp; /* Value which is temporarily replaced with a \0 */ int ierr; /* Error code (aka is 's' a float or not?) */ if (strlen(s) <= len) { return myAtoF(s, value); } else { c_temp = s[len]; s[len] = '\0'; ierr = myAtoF(s, value); s[len] = c_temp; return ierr; } } /***************************************************************************** * myRound() -- Arthur Taylor / MDL * * PURPOSE * Round a number to a given number of decimal places. * * ARGUMENTS * x = number to round (Input) * place = How many decimals to round to (Input) * * RETURNS: double (rounded value) * * HISTORY * 5/2003 Arthur Taylor (MDL/RSIS): Created. * 2/2006 AAT (MDL): Added the (double) (.5) cast, and the mult by * POWERS_OVER_ONE instead of division. * * NOTES * It is probably inadvisable to make a lot of calls to this routine, * considering the fact that a context swap is made, so this is provided * primarily as an example, but it can be used for some rounding. ****************************************************************************/ static double POWERS_ONE[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 }; double myRound(double x, uChar place) { if (place > 17) { place = 17; } return (floor(x * POWERS_ONE[place] + 5e-1)) / POWERS_ONE[place]; /* Have tried the following options to see if I could get some of the * degrib regression test 40 to work on linux, but it appears to cause * other tests to fail on other OS's. */ /* return (((sInt4) (x * POWERS_ONE[place] + .5)) / POWERS_ONE[place]);*/ /* return (floor (x * POWERS_ONE[place] + .5)) / POWERS_ONE[place];*/ /* modf(x * POWERS_ONE[place] + 5e-1, &d_temp); */ /* return (d_temp / POWERS_ONE[place]);*/ } /***************************************************************************** * myDoubleEq() -- Arthur Taylor / MDL * * PURPOSE * Determine if 2 doubles are equal to a given number of decimal places. * * ARGUMENTS * x = 1st number to compare (Input) * y = 2nd number to compare (Input) * place = How many decimals to round to (Input) * * RETURNS: int * True if x == y, false if x != y * * HISTORY * 4/2007 Arthur Taylor (MDL): Created. * * NOTES * It is probably inadvisable to make a lot of calls to this routine, * considering the fact that a context swap is made, so this is provided * primarily as an example, but it can be used for some rounding. ****************************************************************************/ int myDoubleEq(double x, double y, uChar place) { if (place > 17) { place = 17; } return (fabs(x - y) < 1. / POWERS_ONE[place]); } /***************************************************************************** * strTrim() -- Arthur Taylor / MDL * * PURPOSE * Trim the white space from both sides of a char string. * * ARGUMENTS * str = The string to trim (Input/Output) * * RETURNS: void * * HISTORY * 10/2003 Arthur Taylor (MDL/RSIS): Created. * 11/2006 Joe Lang and Arthur Taylor (MDL): Modified for all space error. * * NOTES * See K&R p106 for strcpy part. * Also: http://icecube.wisc.edu/~dglo/c_class/strmemfunc.html * Also: google with "strcpy memory overlap" ****************************************************************************/ void strTrim(char *str) { char *ptr; /* Pointer to where first non-white space is. */ char *ptr2; /* Pointer to just past last non-white space. */ if (str == NULL) { return; } /* Trim the string to the left first. */ for (ptr = str; isspace(*ptr); ++ptr) { } /* Did we hit the end of an all space string? */ if (*ptr == '\0') { *str = '\0'; return; } /* now work on the right side. */ for (ptr2 = ptr + (strlen(ptr) - 1); isspace(*ptr2); ptr2--) { } /* adjust the pointer to add the null byte. */ ptr2++; *ptr2 = '\0'; if (ptr != str) { /* Can't do a strcpy here since we don't know that they start at left * and go right. */ while ((*str++ = *ptr++) != '\0') { } *str = '\0'; } } /***************************************************************************** * strToLower() -- Arthur Taylor / MDL * * PURPOSE * Convert a string to all lowercase. * * ARGUMENTS * s = The string to adjust (Input/Output) * * RETURNS: void * * HISTORY * 5/2004 Arthur Taylor (MDL/RSIS): Created. * 2/2007 AAT (MDL): Updated. * * NOTES ****************************************************************************/ void strToLower(char *s) { char *p = s; /* Used to traverse s. */ myAssert(s != NULL); if (s == NULL) { return; } while ((*p++ = tolower(*s++)) != '\0') { } } /***************************************************************************** * strToUpper() -- Arthur Taylor / MDL * * PURPOSE * Convert a string to all uppercase. * * ARGUMENTS * s = The string to adjust (Input/Output) * * RETURNS: void * * HISTORY * 10/2003 Arthur Taylor (MDL/RSIS): Created. * 8/2007 AAT (MDL): Updated. * * NOTES ****************************************************************************/ void strToUpper(char *s) { char *p = s; /* Used to traverse s. */ myAssert(s != NULL); if (s == NULL) { return; } while ((*p++ = toupper(*s++)) != '\0') { } } /***************************************************************************** * ListSearch() -- Arthur Taylor / MDL * * PURPOSE * Looks through a list of strings for a given string. Returns the index * where it found it. * Originally "GetIndexFromStr(cur, UsrOpt, &index);" * now becomes "index = ListSearch(UsrOpt, sizeof(UsrOpt), cur);" * Advantage is that UsrOpt doesn't need a NULL last element. * * ARGUMENTS * List = The list to look for s in. (Input) * N = The length of the List. (Input) * s = The string to look for. (Input) * * RETURNS: int * # = Where s is in List. * -1 = Couldn't find it. * * HISTORY * 9/2002 Arthur Taylor (MDL/RSIS): Created. * 12/2002 (TK,AC,TB,&MS): Code Review. * 2/2007 AAT (MDL): Updated. * 10/2007 AAT: Added check to see if *List was NULL before the strcmp * * NOTES * Originally: GetIndexFromStr (cur, UsrOpt, &index) * => index = ListSearch (UsrOpt, sizeof (UsrOpt), cur); ****************************************************************************/ int ListSearch(char **List, size_t N, const char *s) { int cnt = 0; /* Current Count in List. */ myAssert(s != NULL); if (s == NULL) { return -1; } myAssert(List != NULL); for (; cnt < N; ++List, ++cnt) { if (*List == NULL) { break; } if (strcmp(s, *List) == 0) { return cnt; } } return -1; } /***************************************************************************** * fileAllocNewExten() -- Arthur Taylor / MDL * * PURPOSE * Replace the extension of a filename with the given extension, by copying * the old filename, without the extension (if there is one), to newly * allocated memory, and then strcat the extension on. * * ARGUMENTS * name = The orignal filename to work with. (Input) * ext = The file extension to replace the old one with, or add (Input) * newName = The newly allocated and copied to memory (Output) * * RETURNS: int * 0 = OK * -1 = Memory allocation error. * * HISTORY * 3/2007 Arthur Taylor (MDL): Created. * * NOTES ****************************************************************************/ int fileAllocNewExten(const char *name, const char *ext, char **newName) { char *ptr; /* Used to find the last '.' in the filename */ if ((ptr = strrchr(name, '.')) == NULL) { *newName = (char *)malloc((strlen(name) + strlen(ext) + 1) * sizeof(char)); if (*newName == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } strcpy(*newName, name); } else { *newName = (char *)malloc(((ptr - name) + strlen(ext) + 1) * sizeof(char)); if (*newName == NULL) { myWarn_Err1Arg("Ran out of memory\n"); return -1; } strncpy(*newName, name, ptr - name); (*newName)[ptr - name] = '\0'; } strcat(*newName, ext); return 0; } /***************************************************************************** * myCyclicBounds() -- Arthur Taylor / MDL * * PURPOSE * Retun a value within the bounds of [min...max] by adding or subtracting * the range of max - min. * * ARGUMENTS * value = The orignal filename to work with. (Input) * min = The minimum value of the range. (Input) * max = The maximum value of the range. (Input) * * RETURNS: double * The value that falls in the range of [min..max] * * HISTORY * 3/2007 Arthur Taylor (MDL): Created. * * NOTES ****************************************************************************/ double myCyclicBounds(double value, double min, double max) { while (value < min) { value += max - min; } while (value > min) { value -= max - min; } return value; } /***************************************************************************** * myCntNumLines() -- Arthur Taylor / MDL * * PURPOSE * Counts the number of new lines in an open file, then rewinds to the * beginning of the file, and returns the count + 1, since the last line * might not have a new line. * * ARGUMENTS * fp = An open file pointer to look at. (Input) * * RETURNS: size_t * The number of new lines in the file + 1. * * HISTORY * 3/2007 Arthur Taylor (MDL): Created. * * NOTES ****************************************************************************/ size_t myCntNumLines(FILE *fp) { int c; /* Current char read from stream. */ size_t ans = 1; /* Current count of number of lines in the file. */ while ((c = getc(fp)) != EOF) { if (c == '\n') { ans++; } } rewind(fp); return ans; }