arrays.c

Go to the documentation of this file.
00001 
00007 #include "gclibo.h"
00008 
00010 struct H_ArrayData
00011 {
00012   char name[16]; //copy of array name
00013   char* data; //pointer to the ASCII array data
00014   int len; //length of data
00015   int elements; //found data elements for properly dimensioning on download
00016   int index; //access index into data for write/read operations
00017 
00018   struct H_ArrayData* next; //pointer to next ArrayNode in the list
00019 
00020   //The following fields are only valid on the head node
00021   struct H_ArrayData * tail; //if this node is the head node, last ptr will be maintained for faster tail insertion
00022   int count; //if this node is the head node, count will be maintained for total number of arrays
00023   //note, head node also holds array data
00024 };
00025 typedef struct H_ArrayData ArrayNode;
00026 
00028 void H_InitArrayNode(ArrayNode* node)
00029 {
00030   node->count = 0;
00031   node->data = 0;
00032   node->index = 0;
00033   node->len = 0;
00034   node->name[0] = 0;
00035   node->next = 0; //null indicates end of list
00036   node->tail = 0;
00037   node->elements = 0;
00038   //could memset to zero...
00039 }
00040 
00042 GReturn H_AddArray(ArrayNode* head, char* name, char* data)
00043 {
00044   ArrayNode* node; //the node to fill with data
00045   if (head->count == 0) //no need to malloc, just fill the head
00046     node = head;
00047   else
00048   {
00049     node = malloc(sizeof(ArrayNode));
00050     if (node) //malloc ok
00051       H_InitArrayNode(node);
00052     else
00053       return G_BAD_FULL_MEMORY; //malloc failed
00054   }
00055 
00056   node->data = data; //copy pointer
00057   strcpy(node->name, name); //copy name array
00058   node->len = strlen(node->data); //output of GArrayUpload is null terminated.
00059 
00060   head->count++; //count the node we just made
00061   head->tail->next = node; //link the new node. If node == head this breaks the last-node-next-null guarantee
00062   node->next = 0; //enforce the last-node-next-null guarantee
00063   head->tail = node; //update head's tail pointer
00064   
00065   return G_NO_ERROR;
00066 }
00067 
00069 void H_FreeArrays(ArrayNode* node)
00070 {
00071   if (node == 0) return; //recursive exit condition
00072   free(node->data); //free this node's data
00073   H_FreeArrays(node->next); //let downstream nodes recursvely free their data
00074   free(node->next); //free the struct this node points to
00075   //no need to free node, the head is declared on the stack
00076 }
00077 
00079 GReturn H_UploadArrayToList(GCon g, ArrayNode* head, char* name)
00080 {
00081   GReturn rc = G_NO_ERROR; //return code
00082   char* array_buf; //buffer to hold array as it's uploaded
00083   if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
00084     return G_BAD_FULL_MEMORY;
00085 
00086   if ((rc = GArrayUpload(g, name, G_BOUNDS, G_BOUNDS, G_CR, array_buf, MAXARRAY)) != G_NO_ERROR) //get this array's data
00087     return rc;
00088 
00089   return H_AddArray(head, name, array_buf); //push the data into the array linked list
00090 }
00091 
00093 GReturn H_CreateArrayNode(ArrayNode* head, char* name)
00094 {
00095   char* array_buf; //buffer to hold array data
00096   if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
00097     return G_BAD_FULL_MEMORY;
00098   array_buf[0] = 0; //null terminate so len is correct when added
00099   return H_AddArray(head, name, array_buf); //push the data into the array linked list
00100 }
00101 
00103 GReturn H_ArrayAddElement(ArrayNode* node, GCStringIn element)
00104 {
00105   int len = strlen(element);
00106   if ((len + node->index + 1) >= MAXARRAY) //+1 for \r
00107     return G_BAD_FULL_MEMORY;
00108 
00109   strcpy(node->data + node->index, element); //copy the data to the array
00110   node->index += len;
00111   node->data[node->index++] = '\r'; //delim
00112   node->data[node->index] = 0; //null terminate
00113   node->len = node->index; //maintain len field 
00114   node->elements++; //count the element just added
00115   return G_NO_ERROR;
00116 }
00117 
00119 
00126 GReturn H_DownloadArraysFromList(GCon g, ArrayNode* head)
00127 {
00128   ArrayNode* node = head;
00129   GReturn rc = G_NO_ERROR;
00130   char command[32]; //buffer for holding command calls
00131   while ((node != 0) && (rc == G_NO_ERROR))
00132   {
00133 
00134     //*** Start array table modification
00135     //    Deallocate the array in case it's the wrong size.
00136     sprintf(command, "DA %s[]", node->name);
00137     if ((rc = GCmd(g, command)) != G_NO_ERROR)
00138       return rc;
00139 
00140     //    Dimension the array with the correct length.
00141     sprintf(command, "DM %s[%i]", node->name, node->elements);
00142     if ((rc = GCmd(g, command)) != G_NO_ERROR)
00143       return rc;
00144     //*** End array table modification
00145 
00146     rc = GArrayDownload(g, node->name, G_BOUNDS, G_BOUNDS, node->data); //download the array
00147     node = node->next;
00148   }
00149   return rc;
00150 }
00151 
00153 GReturn H_WriteArrayCsv(ArrayNode* head, GCStringIn file_path)
00154 {
00155   if (head->count == 0) //nothing to do
00156     return G_NO_ERROR;
00157 
00158   FILE *file; //file pointer
00159   size_t bytes; //length of data to write
00160   size_t bytes_written; //bytes actually written to file
00161   int colcount = 0; //column counter, used to prevent a trailing colon
00162   int data_left = head->count; //counter for number of arrays that still have data left to be written
00163   ArrayNode* node = head; //pointer to an array node in the list
00164 
00165   if (!(file = fopen(file_path, "wb"))) //open file for writing, binary mode
00166     return G_BAD_FILE;
00167 
00168   //write the header
00169   do
00170   {
00171     bytes = strlen(node->name);
00172     bytes_written = fwrite(node->name, 1, bytes, file);
00173     colcount++;
00174 
00175     if (colcount != head->count) //write a comma if it's not the last column
00176     {
00177       bytes_written += fwrite(",", 1, 1, file);
00178       bytes++;
00179     }
00180     else //write a carriage return
00181     {
00182       bytes_written += fwrite("\r", 1, 1, file);
00183       bytes++;
00184     }
00185 
00186     if (bytes_written != bytes) //ensure we wrote what we wanted
00187     {
00188       fclose(file);
00189       return G_BAD_FILE;
00190     }
00191     node = node->next;
00192   } while (node != 0);
00193 
00194 
00195   //now write the data
00196   while (data_left) //continue writing rows as long as arrays have data to write
00197   {
00198     node = head;
00199     colcount = 0;
00200     do //write one row
00201     {
00202       bytes_written = 0;
00203       bytes = 0;
00204       if (node->index != node->len) //data available
00205       {
00206         while ((node->data[node->index] != '\r') //search for the carriage return delim
00207           && (node->index < node->len)) //unless we reach the end
00208         {
00209           if (node->data[node->index] != ' ') //don't keep spaces
00210           {
00211             bytes_written += fwrite(node->data + node->index, 1, 1, file);
00212             bytes++;
00213           }
00214           node->index++;
00215         }
00216 
00217         if (node->index == node->len) //reached the end of this data
00218           data_left--; //decrement counter to indicate one less array with data to write
00219 
00220         node->index++; //jump over \r delim
00221       }
00222 
00223       colcount++; //count the cell we just filled
00224       if (colcount != head->count) //write a comma if it's not the last column
00225       {
00226         bytes_written += fwrite(",", 1, 1, file);
00227         bytes++;
00228       }
00229       else //write a carriage return, even on the last line
00230       {
00231         bytes_written += fwrite("\r", 1, 1, file);
00232         bytes++;
00233       }
00234 
00235       //check for write failure
00236       if (bytes_written != bytes)
00237       {
00238         fclose(file);
00239         return G_BAD_FILE;
00240       }
00241 
00242       node = node->next;
00243     } while (node != 0);
00244   } //while (data_left)
00245 
00246   fclose(file);
00247   return G_NO_ERROR;
00248 }
00249 
00250 
00251 GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
00252 {
00253   FILE *file;
00254   GReturn rc = G_NO_ERROR;
00255   char name[32]; //buffer for holding name of array
00256   int n; //index into name
00257   char element[32]; //buffer for holding ascii array element value
00258   int e; //index into element
00259   char c; //char currently being read
00260 
00261   //Linked list to hold array data as it's organized for download
00262   ArrayNode head; //first element (list head) lives on this stack
00263   ArrayNode* node; //current node
00264   H_InitArrayNode(&head);
00265   head.tail = &head; //circular reference
00266 
00267   if (!(file = fopen(file_path, "rb"))) //open file for reading, binary mode
00268     return G_BAD_FILE;
00269 
00270   //read out header, making a new ArrayNode for each 
00271   n = 0;
00272   c = 0;
00273   while (fread(&c, 1, 1, file))
00274   {
00275     if ((c == ',') || (c == '\r'))
00276     {
00277       if (n)
00278       {
00279         name[n] = 0; //null terminate
00280         n = 0; // next time start filling name from start
00281         H_CreateArrayNode(&head, name);
00282       }
00283 
00284       if (c == '\r') //end of line
00285         break; //done reading headers
00286     }
00287     else
00288     {
00289       name[n++] = c;
00290     }
00291   }
00292 
00293   //read each line of the file, pushing each cell into its corresponding ArrayNode
00294   node = &head;
00295   e = 0;
00296   while (fread(&c, 1, 1, file))
00297   {
00298     if ((c == ',') || (c == '\r'))
00299     {
00300       if (e) //if anything read into element
00301       {
00302         element[e] = 0; //null terminate
00303         H_ArrayAddElement(node, element);
00304         e = 0; //start writing at start of element on next pass
00305       }
00306       node = node->next; //go to the next array
00307       if (node == 0) node = &head; //wrap around to front
00308     }
00309     else
00310     {
00311       element[e++] = c;
00312     }
00313   }
00314 
00315 
00316 
00317   fclose(file); //done with file
00318 
00319   //By here, all the array data is in the linked-list starting at head, just download it
00320   rc = H_DownloadArraysFromList(g, &head);
00321   H_FreeArrays(&head); // don't forget to free memory
00322   return rc;
00323 }
00324 
00325 
00326 GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
00327 {
00328 
00329   GReturn rc = G_NO_ERROR; //return code
00330   long bytes; //strlen placeholder
00331   char array_names[1024]; //buffer to hold copy of names, or response to list arrays LA
00332   char name[32]; //buffer to hold a single array name
00333   int i, n; //indices
00334   char c; //holder for the char currently being read
00335   int bracket = 0; //increments when [ seen on a line, [ marks the end of the array name
00336 
00337   //Linked list to hold array data
00338   ArrayNode head; //first element (list head) lives on this stack
00339   H_InitArrayNode(&head);
00340   head.tail = &head; //circular reference
00341 
00342   if (names == 0) //check for null pointer in arg
00343     bytes = 0;
00344   else
00345     bytes = strlen(strcpy(array_names, names));
00346 
00347   if (bytes == 0) //null or "", need to get the arrays from the controller
00348   {
00349     if ((rc = GCmdT(g, "LA", array_names, sizeof(array_names), 0)) != G_NO_ERROR) //Trimming command, get names from List Arrays (LA)
00350       return rc; //no mallocs yet, so we can exit without free
00351 
00352     bytes = strlen(array_names); //count the response
00353   }
00354 
00355   n = 0; //n is name[] index
00356   for (i = 0; i < bytes; i++)
00357   {
00358     c = array_names[i];
00359 
00360     if (c == '[')
00361       bracket++; //[ marks the end of the array name
00362     
00363     if ((c != ' ') && (c != '\r') && (c != '\n') && !bracket)
00364     {
00365       name[n++] = array_names[i]; //keep the char
00366     }
00367     
00368     
00369     if ((c == ' ') || (c == '\r') || (i == bytes - 1))
00370     {
00371       if (n) //if we have anything in name
00372       {
00373         name[n] = 0; // null terminate name
00374         n = 0; // next time start filling name from start
00375         bracket = 0; //forget any brackets we've seen
00376 
00377         if ((rc = H_UploadArrayToList(g, &head, name)) != G_NO_ERROR) //Add data to list
00378         {
00379           H_FreeArrays(&head); // don't forget to free memory
00380           return rc;
00381         }
00382       }
00383       continue;
00384     }
00385 
00386   }
00387 
00388   //By here, all the array data is in the linked-list starting at head.
00389   rc = H_WriteArrayCsv(&head, file_path);
00390   H_FreeArrays(&head); // don't forget to free memory
00391   return rc;
00392 }