strcollection.c

Discussion in 'C Programming' started by jacob navia, Oct 4, 2009.

  1. jacob navia

    jacob navia Guest

    /*------------------------------------------------------------------------
    Module: strcollection.c
    Author: jacob
    Project: Containers
    State:
    Creation Date:
    Description: Implements the string collection container.
    Added fixes from Gerome. Oct. 20 2005
    ------------------------------------------------------------------------*/
    #include "containers.h"
    static int GetCount( StringCollection *SC);
    static int IsReadOnly( StringCollection *SC);
    static int SetReadOnly( StringCollection *SC,int newval);
    static int Add( StringCollection *SC,char *newval);
    static int AddRange( StringCollection *SC,char **newvalues);
    static int Clear( StringCollection *SC);
    static bool Contains( StringCollection *SC,char *str);
    static char **CopyTo( StringCollection *SC);
    static int IndexOf( StringCollection *SC,char *SearchedString);
    static int Insert( StringCollection *SC,char *);
    static int InsertAt( StringCollection *SC,int idx,char *newval);
    static char *IndexAt( StringCollection *SC,int idx);
    static int Remove( StringCollection *SC,char *);
    static int RemoveAt( StringCollection *SC,int idx);
    static int Finalize( StringCollection *SC);
    static int GetCapacity( StringCollection *SC);
    static bool SetCapacity( StringCollection *SC,int newCapacity);
    static void Apply(StringCollection *SC,int(*Applyfn)(char *,void *),void *arg);
    static int *Map(StringCollection *SC,int (*Applyfn)(char *));
    static int Push(StringCollection *SC,char *str);
    static char *Pop(StringCollection *SC);
    static char *ReplaceAt(StringCollection *SC,int idx,char *newval);
    static int IsCaseSensitive(StringCollection *SC);
    static int SetCaseSensitive(StringCollection *SC,int newval);
    static bool Equal(StringCollection *SC1,StringCollection *SC2);
    static StringCollection *Copy(StringCollection *SC);
    static StringCollectionInterface lpVtblSC = {
    GetCount, IsReadOnly, SetReadOnly, Add, AddRange, Clear, Contains,
    CopyTo, IndexOf, Insert, InsertAt, IndexAt, Remove, RemoveAt,
    Finalize, GetCapacity, SetCapacity, Apply, Map, Push, Pop, ReplaceAt,
    IsCaseSensitive,
    SetCaseSensitive,Equal,Copy,
    };

    #ifndef DEFAULT_START_SIZE
    #define DEFAULT_START_SIZE 20
    #endif
    /*------------------------------------------------------------------------
    Procedure: DuplicateString ID:1
    Purpose: Functional equivalent of strdup with added argument
    in case of error
    Input: The string to duplicate and the name of the calling function
    Output: The duplicated string
    Errors: Duplication of NULL is allowed and returns NULL. If no more
    memory is available the error function is called.
    ------------------------------------------------------------------------*/
    static char * DuplicateString(char *str,const char *fnname)
    {
    char *result;
    if (str == NULL) return NULL;
    result = MALLOC(strlen(str)+1);
    if (result == NULL) {
    ContainerRaiseError(fnname,CONTAINER_ERROR_NOMEMORY);
    return NULL;
    }
    strcpy(result,str);
    return result;
    }

    #define SC_READONLY 1
    #define SC_IGNORECASE 2
    #define CHUNKSIZE 20

    /*------------------------------------------------------------------------
    Procedure: newStringCollection ID:1
    Purpose: Creates a new string collection
    Input: The initial start size of the collection
    Output: A pointer to the new collection or NULL
    Errors: If no more memory is available it returns NULL
    after calling the error function.
    ------------------------------------------------------------------------*/
    StringCollection *newStringCollection(int startsize)
    {
    StringCollection *result;

    result = MALLOC(sizeof(*result));
    if (result == NULL) {
    ContainerRaiseError("newStringCollection",CONTAINER_ERROR_NOMEMORY);
    return NULL;
    }
    memset(result,0,sizeof(StringCollection));
    result->lpVtbl = &lpVtblSC;
    if (startsize > 0) {
    result->contents = MALLOC(startsize*sizeof(char *));
    if (result->contents == NULL) {
    ContainerRaiseError("newStringCollection",CONTAINER_ERROR_NOMEMORY);
    FREE(result);
    result=NULL;
    }
    else {
    memset(result->contents,0,sizeof(char *)*startsize);
    result->capacity = startsize;
    result->RaiseError = ContainerRaiseError;
    }
    }
    return result;
    }

    static int GetCount(struct _StringCollection *SC){ return SC->count;}

    /*------------------------------------------------------------------------
    Procedure: IsReadOnly ID:1
    Purpose: Reads the read-only flag
    Input: The collection
    Output: the state of the flag
    Errors: None
    ------------------------------------------------------------------------*/
    static int IsReadOnly(StringCollection *SC)
    {
    return (SC->Flags & SC_READONLY) ? 1 : 0;
    }

    /*------------------------------------------------------------------------
    Procedure: SetReadOnly ID:1
    Purpose: Setsq the value of the read only flag
    Input: The collection to be changed
    Output: The old value of the flag
    Errors: None
    ------------------------------------------------------------------------*/
    static int SetReadOnly(StringCollection *SC,int newval)
    {
    int oldval = SC->Flags & SC_READONLY ? 1 : 0;
    if (newval)
    SC->Flags |= SC_READONLY;
    else
    SC->Flags &= ~SC_READONLY;
    return oldval;
    }

    /*------------------------------------------------------------------------
    Procedure: IsCaseSensitive ID:1
    Purpose: Reads the case sensitive flag
    Input: The collection
    Output: The state of the flag
    Errors: None
    ------------------------------------------------------------------------*/
    static int IsCaseSensitive(StringCollection *SC)
    {
    return (SC->Flags & SC_IGNORECASE) ? 0 : 1;
    }

    /*------------------------------------------------------------------------
    Procedure: SetReadOnly ID:1
    Purpose: Setsq the value of the read only flag
    Input: The collection to be changed
    Output: The old value of the flag
    Errors: None
    ------------------------------------------------------------------------*/
    static int SetCaseSensitive(StringCollection *SC,int newval)
    {
    int result = (SC->Flags & SC_IGNORECASE) ? 0 : 1;
    if (newval) SC->Flags &= ~SC_IGNORECASE;
    else
    SC->Flags |= SC_IGNORECASE;
    return result;
    }

    /*------------------------------------------------------------------------
    Procedure: Resize ID:1
    Purpose: Grows a string collection by CHUNKSIZE
    Input: The collection
    Output: One if successfull, zero othewise
    Errors: If no more memory is available, nothing is changed
    and returns zero
    ------------------------------------------------------------------------*/
    static int Resize(StringCollection *SC)
    {
    int newcapacity = SC->capacity + CHUNKSIZE;
    char **oldcontents = SC->contents;
    SC->contents = MALLOC(newcapacity*sizeof(char *));
    if (SC->contents == NULL) {
    SC->contents = oldcontents;
    return 0;
    }
    memset(SC->contents,0,sizeof(char *)*newcapacity);
    memcpy(SC->contents,oldcontents,SC->count*sizeof(char *));
    SC->capacity = newcapacity;
    FREE(oldcontents);
    return 1;
    }

    /*------------------------------------------------------------------------
    Procedure: Add ID:1
    Purpose: Adds a string to the string collection.
    Input: The collection and the string to be added to it
    Output: Number of items in the collection or <= 0 if error
    Errors:
    ------------------------------------------------------------------------*/
    static int Add(StringCollection *SC,char *newval)
    {
    if (SC->Flags & SC_READONLY)
    return CONTAINER_ERROR_READONLY;
    if (SC->count >= SC->capacity) {
    int r = Resize(SC);
    if (r <= 0)
    return r;
    }

    if (newval) {
    SC->contents[SC->count] = DuplicateString(newval,"Add");
    if (SC->contents[SC->count] == NULL) {
    return 0;
    }
    }
    else
    SC->contents[SC->count] = NULL;
    return ++SC->count;
    }

    static int AddRange(StringCollection *SC,char **data){
    int i = 0,c;
    if (SC->Flags & SC_READONLY)
    return CONTAINER_ERROR_READONLY;
    c = SC->count;
    while (data != NULL) {
    int r = Add(SC,data);
    if (r <= 0) {
    return r;
    }
    i++;
    }
    return i;
    }

    static int Clear(StringCollection *SC){
    int oldval = SC->count,i;
    if (SC->Flags & SC_READONLY)
    return CONTAINER_ERROR_READONLY;
    for (i=0; i<SC->count;i++) {
    FREE(SC->contents);
    SC->contents = NULL;
    }
    SC->count = 0;
    return oldval;
    }

    static bool Contains(StringCollection *SC,char *str){
    int c,i;
    if (str == NULL) return 0;
    c = *str;
    if ((SC->Flags & SC_IGNORECASE) == 0) {
    for (i=0; i<SC->count;i++) {
    if (c == SC->contents[0] && !strcmp(SC->contents,str)) return 1;
    }
    }
    else {
    for (i = 0; i<SC->count;i++) {
    if (!stricmp(SC->contents,str))
    return 1;
    }
    }
    return 0;
    }

    static char **CopyTo(StringCollection *SC)
    {
    char **result = MALLOC((1+SC->count)*sizeof(char *));
    int i;
    if (result == NULL)
    return NULL;
    for (i=0; i<SC->count;i++) {
    result = DuplicateString(SC->contents,"Copy");
    }
    result = NULL;
    return result;
    }

    static int IndexOf(StringCollection *SC,char *str){
    int i;
    int (*cmpfn)(const char *,const char *);

    if (SC->Flags & SC_IGNORECASE) {
    cmpfn = stricmp;
    }
    else cmpfn = strcmp;

    for (i=0; i<SC->count;i++) {
    if (!cmpfn(SC->contents,str)) {
    return i;
    }
    }
    return -1;
    }

    static char *IndexAt(StringCollection *SC,int idx)
    {
    if (idx >=SC->count || idx < 0)
    return NULL;
    return SC->contents[idx];
    }

    static int InsertAt(StringCollection *SC,int idx,char *newval)
    {
    char *p;
    if (SC->Flags & SC_READONLY)
    return CONTAINER_ERROR_READONLY;
    if (SC->count >= (SC->capacity-1)) {
    int r = Resize(SC);
    if (r <= 0)
    return r;
    }
    if (idx < 0)
    return CONTAINER_ERROR_BADARG;
    if (idx >= SC->count)
    return CONTAINER_ERROR_INDEX;
    p = DuplicateString(newval,"InsertAt");
    if (p == NULL) {
    return CONTAINER_ERROR_NOMEMORY;
    }

    if (idx == 0) {
    if (SC->count > 0)
    memmove(SC->contents+1,SC->contents,SC->count*sizeof(char *));
    SC->contents[0] = p;
    }
    else if (idx == SC->count) {
    SC->contents[idx] = p;
    }
    else if (idx < SC->count) {
    memmove(SC->contents+idx+1,SC->contents+idx,(SC->count-idx+1)*sizeof(char *));
    SC->contents[idx] = p;
    }
    return ++SC->count;
    }

    static int Insert(StringCollection *SC,char *newval)
    {
    return InsertAt(SC,0,newval);
    }

    static int RemoveAt(StringCollection *SC,int idx)
    {
    if (idx >= SC->count || idx < 0 )
    return CONTAINER_ERROR_INDEX;
    if (SC->Flags & SC_READONLY)
    return CONTAINER_ERROR_READONLY;
    /* Test for remove of an empty collection */
    if (SC->count == 0)
    return 0;
    FREE(SC->contents[idx]);
    if (idx < (SC->count-1)) {
    memmove(SC->contents+idx,SC->contents+idx+1,(SC->count-idx)*sizeof(char *));
    }
    SC->contents[SC->count-1]=NULL;
    return --SC->count;
    }

    static int Remove(StringCollection *SC,char *str)
    {
    int idx = IndexOf(SC,str);
    if (idx < 0)
    return CONTAINER_ERROR_NOTFOUND;
    return RemoveAt(SC,idx);
    }

    static int Push(StringCollection *SC,char *str)
    {
    char *r;
    if (SC->Flags&SC_READONLY)
    return 0;
    if (SC->count >= SC->capacity-1) {
    int r = Resize(SC);
    if (r <= 0)
    return r;
    }
    r = DuplicateString(str,"Push");
    if (r == NULL)
    return 0;
    SC->contents[SC->count++] = r;
    return SC->count;
    }

    static char * Pop(StringCollection *SC)
    {
    char *result;
    if ((SC->Flags&SC_READONLY) || SC->count == 0)
    return NULL;
    SC->count--;
    result = SC->contents[SC->count];
    SC->contents[SC->count] = NULL;
    return result;
    }

    static int Finalize(StringCollection *SC){
    int result = SC->count,i;
    for (i=0; i<SC->count;i++) {
    FREE(SC->contents);
    }
    FREE(SC->contents);
    FREE( SC);
    return result;
    }

    static int GetCapacity(StringCollection *SC)
    {
    return SC->capacity;
    }

    static bool SetCapacity(StringCollection *SC,int newCapacity)
    {
    if (SC->count != 0)
    return 0;
    FREE(SC->contents);
    SC->contents = MALLOC(newCapacity*sizeof(char *));
    memset(SC->contents,0,sizeof(char *)*newCapacity);
    SC->capacity = newCapacity;
    return 1;
    }

    static void Apply(StringCollection *SC,int (*Applyfn)(char *,void *),void *arg){
    int i;
    for (i=0; i<SC->count;i++) {
    Applyfn(SC->contents,arg);
    }
    }

    static int *Map(StringCollection *SC,int (*Applyfn)(char *)){
    int *result = MALLOC(SC->count*sizeof(int)),i;

    if (result == NULL) return NULL;
    for (i=0; i<SC->count;i++) {
    result = Applyfn(SC->contents);
    }
    return result;
    }

    /* (gerome) proposed calling DuplicateString. Good
    suggestion */
    static char *ReplaceAt(StringCollection *SC,int idx,char *newval){
    char *r;
    if (SC->Flags & SC_READONLY) return NULL;
    if (idx < 0 || idx > SC->count) return NULL;
    FREE(SC->contents[idx]);
    r = DuplicateString(newval,"ReplaceAt");
    if (r == NULL) return 0;
    SC->contents[idx] = r;
    return newval;
    }

    static bool Equal(StringCollection *SC1,StringCollection *SC2){
    int i;
    if (SC1->count != SC2->count) return 0;
    if (SC1->count == 0) return 1;
    for (i=0; i<SC1->count;i++) {
    if (strcmp(SC1->contents,SC2->contents)) return 0;
    }
    return 1;
    }

    static StringCollection *Copy(StringCollection *SC){
    int i;
    StringCollection *result = newStringCollection(SC->count);

    for (i=0; i<SC->count;i++) {
    if (Add(result,SC->contents) <= 0) {
    Finalize(result);
    break;
    }
    }
    return result;
    }
     
    jacob navia, Oct 4, 2009
    #1
    1. Advertising

  2. jacob navia <> writes:
    <snip>
    > if (!stricmp(SC->contents,str))


    stricmp is not standard C. lcc-win32 (in conforming mode) should warn
    that this function has no prototype in scope.

    --
    Ben.
     
    Ben Bacarisse, Oct 5, 2009
    #2
    1. Advertising

  3. jacob navia

    Seebs Guest

    On 2009-10-05, Ben Bacarisse <> wrote:
    > jacob navia <> writes:
    ><snip>
    >> if (!stricmp(SC->contents,str))


    > stricmp is not standard C. lcc-win32 (in conforming mode) should warn
    > that this function has no prototype in scope.


    I'm not sure of this. Since str[a-z].* (regex notation, for those wondering
    about the '.') are reserved by the standard, I don't think there's any
    obligation for the implementation to diagnose the invocation of a call in
    that namespace which it happens to define.

    -s
    --
    Copyright 2009, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
     
    Seebs, Oct 5, 2009
    #3
  4. Seebs <> writes:

    > On 2009-10-05, Ben Bacarisse <> wrote:
    >> jacob navia <> writes:
    >><snip>
    >>> if (!stricmp(SC->contents,str))

    >
    >> stricmp is not standard C. lcc-win32 (in conforming mode) should warn
    >> that this function has no prototype in scope.

    >
    > I'm not sure of this. Since str[a-z].* (regex notation, for those wondering
    > about the '.') are reserved by the standard, I don't think there's any
    > obligation for the implementation to diagnose the invocation of a call in
    > that namespace which it happens to define.


    I'm not sure either, anymore!

    However, since a compiler can warn about anything it likes, I'd
    suggest that it would be useful if lcc-win32 could be asked to issue a
    warning in these cases. As a QOI issue, I really like to be told when
    I am relying on non-standard functions.

    --
    Ben.
     
    Ben Bacarisse, Oct 5, 2009
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page