/* *This is the root component of the SqueakEasy. *[Implementation] * * Copyright 2003 Joseph Pingenot This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * */ #include #include #include #include #include "readline.h" /*string chunk handling. pretty straightforward. they're put here for ease of * reading and maintenance.*/ void append_stringchunk(struct stringchunk **head, struct stringchunk *newchunk){ /*the current chunk*/ struct stringchunk *curchunk = *head; /*Walk through the list, appending the string chunk when we hit the end*/ if(*head == NULL) { /*The head is empty! Just set it and return.*/ *head = newchunk; return; } /*Alright. Start walking the list. Stop if there is no next struct.*/ while(curchunk->next != NULL) curchunk = curchunk -> next; /*Now just set the next member*/ curchunk->next = newchunk; /*done!*/ return; } void free_chunks(struct stringchunk **head){ /*Do we have a valid sub-list? If not, return! *NOTE that this is the recursion terminator.*/ if(*head == NULL) return; /*Recurse. First, free the list below us.*/ free_chunks(&((*head)->next)); /*now free this node.*/ free(*head); return; } int count_chunks(struct stringchunk *head){ return (head == NULL)? 0 : count_chunks(head->next) + 1; } char *assemble_string_from_chunks(struct stringchunk *head, long int *len){ /*Pretty easy. Just run through the string, copying into the new string.*/ int chunks = count_chunks(head); /*The new buffer length has to be chunks*chunklength + 1 (ending nul charc)*/ char *newstring; register long int i; register int blockcounter; /*block number*/ int block; /*offset into the block*/ int offset; /*The block we're examining*/ struct stringchunk *chunk; /*Sanity check: if there *is* no list, then we just return a null string!*/ if(head == NULL){ return ""; } //printf("in assembly function\n"); //printf("chunks=%d\n", chunks); /*Figure the size*/ *len = chunks * SQKEZ_STRINGCHUNKLEN + 1; /*Allocate the array*/ newstring = (char*)malloc(*len); if(newstring == NULL){ fprintf(stderr, "squeakyroot.assemble_string_from_chunks: unable to allocate a new string of length %d! Data will be lost!\n", *len); return NULL; } /*Now start copying!*/ //printf("starting for loop"); for(i = 0; i < *len; i++) { /*Initialize the chunk we're dealing with, and find the offset and block*/ chunk = head; offset = i % SQKEZ_STRINGCHUNKLEN; block = (i - offset)/SQKEZ_STRINGCHUNKLEN + 1; /*Go into the block*/ //printf("finding block"); for(blockcounter = 1; blockcounter < block; blockcounter++) { chunk = chunk->next; } /*Make sure the offset is less than or equal to the block length *If it's greater or equal to the end and we're at the last block, we're just done * before the block is. Copying data now would be wrong.*/ //printf("chunks=%d, offset=%d, block=%d, end=%d\n", chunks, offset, block, chunk->end); if((offset >= chunk->end) && (block == chunks)) break; //printf(" char='%c'\n", (chunk->chunk)[offset]); /*Now copy the char from the block into the string.*/ newstring[i] = (chunk->chunk)[offset]; } /*Now append a tailing zero to the string.*/ newstring[i] = '\0'; /*And return the string.*/ return newstring; } /*Reads in one line. *ARGS: * file descriptor to read from. * pointer to an int to store the length of the string holding the line. *RETURNS: * pointer to the buffer being allocated. * NULL if the file has closed. */ char *readline(int fd, long int *len){ /*Data holding info*/ char databuff[SQKEZ_STRINGCHUNKLEN]; int inchars; /*Number of bytes read*/ register int i; /*Counter for when we're sanitizing data*/ /*Pointer to the head of the string linked list.*/ struct stringchunk *stringlist = NULL; /*If we have characters left over after the newline, we will store a new stringchunk here.*/ static struct stringchunk *remainder; struct stringchunk *newchunk = NULL; /*Offset from the i iterator into the databuff for copying to remainder.*/ int newline_offset = -1; /*Size of the data we should read in. This is a variable, since we might have a remainder * hanging around.*/ int readlen = SQKEZ_STRINGCHUNKLEN; /*initial position to start copying data into the new chunk*/ int chunkstartpos = 0; /*The newly formed string.*/ char *newstring; //printf("hi!\n"); /*Speaking of, do we have a string chunk hanging around?*/ if(remainder != NULL){ //printf("had remainder.\n"); /*We do. Need the read length.*/ readlen = SQKEZ_STRINGCHUNKLEN - remainder->end; //printf(" its end is %d, so readlen is %d.\n", remainder->end, readlen); /*Now assign the remainder to the newchunk.*/ newchunk = remainder; /*And set remainder back to NULL*/ remainder = NULL; /*We just need to remember to catch the fact that we had a remainder.*/ } /*Read in one stringchunk. *Stop reading when we readh the newline or the client quits. */ while((newline_offset < 0) && (inchars = read(fd, &databuff, readlen))){ //printf("read in %d chars (newline_offset=%d)\n", inchars, newline_offset); if(inchars < 0){ /*Error! What action*/ switch(errno){ case EINTR: case EAGAIN: /*No need to report an error with these two. Just silently retry*/ continue; case EBADF: /*This is special. If the file descriptor is bad, we shouldn't have read any *data in anyway. We should return NULL, since the file descriptor is bad *or we have already previously closed the link.*/ return NULL; case EISDIR: case EINVAL: case EFAULT: /*Fatal errors. Close the socket if we can, and leave.*/ perror("(child) squeakyroot.handle_netlink(): read failed fatally!"); /*break out of this switch*/ break; default: perror("(child) squeakyroot.handle_netlink(): read failed for some reason!"); continue; } /*If we reach here, we need to exit the loop*/ break; } /*Create a new stringchunk if we don't have a remainder (signified by the fact that the * newchunk is not NULL*/ if(newchunk == NULL) { /*No remainder. Just copy like normal*/ //printf("no remainder.\n"); newchunk = (struct stringchunk*) malloc(sizeof(struct stringchunk)); if(newchunk == NULL){ /*error malloc'ing!*/ /*warn the user*/ fprintf(stderr, "squeakyroot: unable to obtain a new string chunk! Data is going to be lost!\n"); /*OK. Best thing to do atm is to break out of the loop and return what we can.*/ break; } newchunk->next = NULL; newchunk->end = 0; chunkstartpos = 0; }else{ //printf("got a remainder to deal with.\n"); /*Don't have to allocate, but we need to specify where the chunk starts now. *This allows us to pick up where we left off.*/ chunkstartpos = newchunk->end; } /*Copy the data.*/ for(i = 0; i < inchars; i++) { /*Quick safety conversion.*/ /*If we have a nul char, it would terminate the line early! *Instead of just passing it through, replace the nul with an underscore!*/ if(databuff[i] == '\0') databuff[i] = '_'; //printf("at i=%d, char is '%c'\n", i, databuff[i]); /*Do we have a newline?*/ if(databuff[i] == '\n'){ //printf(" got newline\n"); /*Yep. Set it.*/ newline_offset = i; /*Now set the chunk length. The last valid index is just after here.*/ newchunk->end = i + chunkstartpos; /*Create the new remainder buffer*/ remainder = (struct stringchunk*) malloc(sizeof(struct stringchunk)); if(remainder == NULL){ fprintf(stderr, "squeakyroot: unable to obtain a new string chunk (remainder)! Data is going to be lost!\n"); break; } remainder->next = NULL; remainder->end = 0; }else{ if(newline_offset == -1){ //printf(" copying to chunk\n"); /*No newline yet.*/ (newchunk->chunk)[i+chunkstartpos] = databuff[i]; }else{ //printf(" copying to remainder\n"); /*Newline reached. Copy to the old chunk. Make sure to include the newline offset*/ (remainder->chunk)[i-newline_offset-1] = databuff[i]; } } } /*Set the end*/ if(remainder != NULL){ remainder->end = i - newline_offset - 1; }else{ newchunk->end = i; } /*Append the new chunk to the list.*/ append_stringchunk(&stringlist, newchunk); /*At the end of the loop, we need to now set the newchunk to NULL and the * readlen.*/ newchunk = NULL; readlen = SQKEZ_STRINGCHUNKLEN; /*now go on to the next line.*/ //printf("looping back\n"); } //printf("out of while loop\n"); /*If we still have a piece waiting to be placed in the stack (e.g. remainder followed by EOF, do so now.*/ if(newchunk != NULL) { append_stringchunk(&stringlist, newchunk); newchunk = NULL; }else if(stringlist == NULL){ /*Both newchunk and the stringlist are NULL. We have no string! *Client must have closed connection. No data here!*/ return NULL; } /*Collapse the string chunks into one string.*/ newstring = assemble_string_from_chunks(stringlist, len); //printf("finished assembling string (%s)", newstring); /*Free the blocks.*/ free_chunks(&stringlist); //printf("at end, string is %s\n", newstring); /*If the end of the remainder is at 0, the remainder chunk is empty! *Free the remainder if that's the case.*/ if((remainder != NULL) && (remainder->end < 1)){ free(remainder); remainder = NULL; } /*now return the new string.*/ return newstring; }