//-------------------------------------------------------------------------- // Process a EXIF marker // Describes all the drivel that most digital cameras include... //-------------------------------------------------------------------------- void process_EXIF (char * CharBuf, unsigned int length) { ImageInfo.FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. FocalplaneXRes = 0; FocalplaneUnits = 0; ExifImageWidth = 0; if (ShowTags){ printf("Exif header %d bytes long\n",length); } { // Check the EXIF header component static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; if (memcmp(CharBuf+2, ExifHeader,6)){ ErrExit("Incorrect Exif header"); } } if (memcmp(CharBuf+8,"II",2) == 0){ if (ShowTags) printf("Exif section in Intel order\n"); MotorolaOrder = 0; }else{ if (memcmp(CharBuf+8,"MM",2) == 0){ if (ShowTags) printf("Exif section in Motorola order\n"); MotorolaOrder = 1; }else{ ErrExit("Invalid Exif alignment marker."); } } // Check the next two values for correctness. if (Get16u(CharBuf+10) != 0x2a || Get32u(CharBuf+12) != 0x08){ ErrExit("Invalid Exif start (1)"); } LastExifRefd = CharBuf; // First directory starts 16 bytes in. Offsets start at 8 bytes in. ProcessExifDir(CharBuf+16, CharBuf+8, length-6); // This is how far the interesting (non thumbnail) part of the exif went. ExifNonThumbnailLength = LastExifRefd - CharBuf; // Compute the CCD width, in milimeters. if (FocalplaneXRes != 0){ ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); } if (ShowTags){ printf("Thunbnail size of Exif header: %d\n",length-ExifNonThumbnailLength); } }
/*************************** newListFromInput ********************************** LinkedList newListFromInput() Purpose: Goes through the input file, for each line, creates a widget and an arrival event for that widget and insert it in a list. Parameters: Returns: Functionally: A LinkedList variable populated from the input. Notes: *******************************************************************************/ LinkedList newListFromInput() { /***variables***/ char szInputBuffer[MAX_LINE_SIZE + 1]; Widget widget; Event arrival; LinkedList list; int iClock, iNextArrival, iScanfCnt; //allocate list list = newLinkedList(); iClock = 0; while (fgets(szInputBuffer, MAX_LINE_SIZE, pInputFile) != NULL && iClock <= MAX_ARRIVAL_TIME) //for each line of the input file { if (szInputBuffer[0] == '\n') //if line is empty, ignore { continue; } //populate the widget variable with data from input line and current clock iScanfCnt = sscanf(szInputBuffer, "%ld %d %d %d\n", &(widget.lWidgetNr), &(widget.iStep1tu), &(widget.iStep2tu), &iNextArrival); widget.iArrivalTime = iClock; if (iScanfCnt < 4) //if there's less than 4 tokens in the line { ErrExit(ERR_BAD_INPUT, "%sExpected 4 tokens,received %d successful values\n", szInputBuffer, iScanfCnt); continue; } if (widget.iStep1tu < 0 || widget.iStep2tu < 0 || iNextArrival < 0) //if the values are negative values { ErrExit(ERR_BAD_INPUT, "%sStep1tu, Step2tu, and DeltaArrival cannot be negative values.\n", szInputBuffer, iScanfCnt); continue; } //populate the arrival Event variable arrival.iEventType = EVT_ARRIVAL; arrival.iTime = iClock; arrival.widget = widget; //insert it into the list insertOrderedLL(list, arrival); //increment clock for next input line (next widget) iClock += iNextArrival; } return list; }
Tree newTree() { Tree tree = (Tree)malloc(sizeof(TreeImp)); if (tree == NULL) ErrExit(ERR_ALGORITHM, "malloc allocation error for TreeImp"); tree->pRoot = NULL; return tree; }
QuoteSelection newQuoteSelection() { QuoteSelection quote = (QuoteSelection)malloc(sizeof(QuoteSelectionImp)); if (quote == NULL) ErrExit(ERR_ALGORITHM, "malloc allocation error for QuoteSelectionImp"); quote->iQuoteItemCnt = 0; return quote; }
int main() { int x = 1, y = -1; if (x != y) ErrExit("Found x = %d, y = %d; Expected them to be equal!", x, y); return EXIT_SUCCESS; }
/******************** newTextList ************************************** TextList newTextList() Purpose: Allocates a new TextList and initalizes it. Parameters: n/a Returns: A pointer to the newly allocated TextList. Notes: - This uses an array implementation of TextList. - Initializes the number of entries to 0. **************************************************************************/ TextList newTextList() { TextList textList = (TextList)malloc(sizeof(TextListImp)); if (textList == NULL) ErrExit(ERR_ALGORITHM, "malloc ran out of memory for new TextList"); textList->iNumEntry = 0; return textList; }
/******************** newGraph ************************************** Graph newGraph() Purpose: Allocates a GraphImp and initializes its data. It returns a Graph. Parameters: n/a Notes: - Uses malloc to allocate a GraphImp. It also makes certain that malloc didn't fail. - Initializes iNumvetices to 0. - Initializes the array of vertices to zero bytes. - Returns: Graph - the newly allocated graph. **************************************************************************/ Graph newGraph() { Graph g = (Graph)malloc(sizeof(GraphImp)); if (g == NULL) ErrExit(ERR_ALGORITHM, "malloc for Graph failed"); g->iNumVertices = 0; memset(g->vertexM, '\0', sizeof(Vertex)*MAX_VERTICES); return g; }
EdgeNode *allocateEdgeNode(Edge value) { EdgeNode *pNew; pNew = (EdgeNode *)malloc(sizeof(EdgeNode)); if (pNew == NULL) ErrExit(ERR_ALGORITHM, "No available memory for linked list"); pNew->edge = value; pNew->pNextEdge = NULL; return pNew; }
/******************** allocateNodeT **************************************** NodeT *allocateNodeT(QuoteSelection quote, Element element) Purpose: Allocates memory for a new node in the QuoteSelection tree. Exits the program with an error if no memory is available. Parameters: I/O Quoteselection quote Tree to be modified I Element element Item to be inserted into the tree Returns: pNew Returns a pointer to the new node with pSibling and pChild pointing to NULL ErrExit No memory available for allocation Notes: **************************************************************************/ NodeT *allocateNodeT(Element element) { NodeT *pNew; pNew = (NodeT *)malloc(sizeof(NodeT)); if (pNew == NULL) ErrExit(ERR_ALGORITHM, "No available memory for binary tree."); pNew->element = element; pNew->pChild = NULL; pNew->pSibling = NULL; return pNew; }
/************************* allocateNodeLL ************************************** NodeLL *allocateNodeLL(LinkedList list, Event value) Purpose: Creates and allocate the memory for a new node including the passed by event. Parameters: I LinkedList list The current list. I Event value The element to be included in the new node. Returns: Functionally: A pointer to the new node containing the element we passed it. Notes: *******************************************************************************/ NodeLL *allocateNodeLL(LinkedList list, Event value) { NodeLL *pNew = (NodeLL *) malloc(sizeof(NodeLL)); if (pNew == NULL) { ErrExit(ERR_ALGORITHM, "No available memory for linked list"); } pNew->event = value; pNew->pNext = NULL; return pNew; }
/************************* newLinkedList *************************************** LinkedList newLinkedList() Purpose: Creates and allocates the memory for a new LinkedList with Header Node. Parameters: Returns: Functionally: The newly created list. Notes: Adapted for a LinkedList with Header Node. *******************************************************************************/ LinkedList newLinkedList() { LinkedList list = (LinkedList) malloc(sizeof(LinkedListImp)); if (list == NULL) { ErrExit(ERR_ALGORITHM, "No available memory for linked list"); } NodeLL *pHeader = (NodeLL *) malloc(sizeof(NodeLL)); list->pHead = pHeader; list->pHead->pNext = NULL; return list; }
/****************************** allocNodeQ ************************************* NodeQ *allocNodeQ(Queue queue, QElement value) Purpose: Creates and allocate the memory for a new node including the passed by value. Parameters: I Queue queue The current queue. I QElement value The element to be included in the new node. Returns: Functionally: A pointer to the new node containing the element we passed it. Notes: *******************************************************************************/ NodeQ *allocNodeQ(Queue queue, QElement value) { NodeQ *pNew; pNew = (NodeQ *)malloc(sizeof(NodeQ)); if (pNew == NULL) { ErrExit(ERR_ALGORITHM, "No available memory for queue"); } pNew->element = value; pNew->pNext = NULL; return pNew; }
/******************** getGraphData ************************************** int getGraphData(struct Data dataM[]) Purpose: Populates an array of edges with data from stdin. The array is terminated when EOF or a 0 0 0 is encountered in the data. Parameters: O struct Data dataM[] An array of edges (to, from, path weight) Notes: - The array of edges is terminated by an edge having a from vertex equal to the character '0'. Returns: TRUE - data was read to populate the array of vertices. FALSE - no data found. **************************************************************************/ int getGraphData(struct Data dataM[]) { char szInput[100]; int i = 0; int iScanfCnt; while (fgets(szInput, 50, stdin) != NULL) { if (i > MAX_EDGES) ErrExit(ERR_BAD_INPUT, "too many edges"); iScanfCnt = sscanf(szInput, "%c %c %d" , &dataM[i].cFrom, &dataM[i].cTo, &dataM[i].iPath); if (iScanfCnt < 3) ErrExit(ERR_BAD_INPUT, " Found: '%s', scanf count is %d", szInput, iScanfCnt); if (dataM[i].cFrom == '0') { dataM[i].cFrom = '\0'; break; } i++; } return i > 0; }
/******************************************************************* void runSimulationA(Simulation sim, int iTimeLimit) Purpose: Runs a simulation on the event list. Prints a table displaying the arrival and departures of travelers Parameters: I Simulation simulation I int iTimeLimit Returns: 1. Does not return anything functionally Notes: 1. Uses removeLL function *******************************************************************/ void runSimulationA(Simulation sim, int iTimeLimit) { Event event; // Creates a local event variable to store current // nodes event information into (uses in simulation evaluation) Server server1 = newServer("Server 1"); // Creates a new server - // - contains Server Name, whether it is busy or not, and a widget Server server2 = newServer("Server 2"); // creates the second server for our program (refer to server1 comments) Queue queue1 = newQueue("Queue 1"); // Creates a new queue Queue queue2 = newQueue("Queue 2"); // Creates a new queue if (sim->bVerbose == TRUE) printf("%-4s %-6s %-10s\n", "TIME", "WIDGET", "EVENT"); //table header while (removeLL(sim->eventList, &event)) { // buffer to stop arrivals after the time limit if (event.iEventType == EVT_ARRIVAL && event.iTime > iTimeLimit) continue; sim->iClock = event.iTime; // advance clock to current event time // the switch evaluates the eventType switch(event.iEventType) { case EVT_ARRIVAL: arrival(sim, &event.widget); queueUp(sim, queue1, &event.widget); seize(sim, queue1, server1); break; case EVT_SERVER1_COMPLETE: release(sim, queue1, server1, &event.widget); queueUp(sim, queue2, &event.widget); seize(sim, queue2, server2); break; case EVT_SERVER2_COMPLETE: release(sim, queue2, server2, &event.widget); leaveSystem(sim, event.widget); break; default: ErrExit(ERR_ALGORITHM, "Unknown event type: %d\n", event.iEventType); } } // prints the averages produced by the simulation printStatistics(sim, queue1, queue2); // frees servers and queues used in simulation A free(server1); free(server2); free(queue1); free(queue2); }
/******************** addTextEntry ************************************** void addTextEntry(TextList textList, TextEntry text) Purpose: Adds a text entry to the TextList. It replaces it if it already exists. Parameters: I/O TextList textList A text list which contains many text entries. The text entry is added to this. I TextEntry text The text entry to be added to the text list. Returns: n/a Notes: - This uses an array implementation of TextList. If the number of entries excedes MAX_TEXT_LIST_SIZE, it exits. **************************************************************************/ void addTextEntry(TextList textList, TextEntry text) { // See if it already exists int iFound = findText(textList, text.szId); if (iFound >= 0) { // Replace the text entry textList->arrayM[iFound] = text; return; } // Not found, so try to insert it // Array boundary check if (textList->iNumEntry >= MAX_TEXT_LIST_SIZE) ErrExit(ERR_DATA, "Too many text entries"); textList->arrayM[textList->iNumEntry] = text; textList->iNumEntry++; }
/******************** getToken ************************************** char * getToken (char *pszInputTxt, char szToken[], int iTokenSize) Purpose: Examines the input text to return the next token. It also returns the position in the text after that token. This function does not skip over white space, but it assumes the input uses spaces to separate tokens. Parameters: I char *pszInputTxt input buffer to be parsed O char szToken[] Returned token. I int iTokenSize The size of the token variable. This is used to prevent overwriting memory. The size should be the memory size minus 1 (for the zero byte). Returns: Functionally: Pointer to the next character following the delimiter after the token. NULL - no token found. szToken parm - the returned token. If not found, it will be an empty string. Notes: - If the token is larger than the szToken parm, we return a truncated value. - If a token isn't found, szToken is set to an empty string - This function does not skip over white space occurring prior to the token. **************************************************************************/ char * getToken(char *pszInputTxt, char szToken[], int iTokenSize) { int iDelimPos; // found position of delim int iCopy; // number of characters to copy char szDelims[20] = " \n\r"; // delimiters szToken[0] = '\0'; // check for NULL pointer if (pszInputTxt == NULL) ErrExit(ERR_ALGORITHM , "getToken passed a NULL pointer"); // Check for no token if at zero byte if (*pszInputTxt == '\0') return NULL; // get the position of the first delim iDelimPos = strcspn(pszInputTxt, szDelims); // if the delim position is at the first character, return no token. if (iDelimPos == 0) return NULL; // see if we have more characters than target token, if so, trunc if (iDelimPos > iTokenSize) iCopy = iTokenSize; // truncated size else iCopy = iDelimPos; // copy the token into the target token variable memcpy(szToken, pszInputTxt, iCopy); szToken[iCopy] = '\0'; // null terminate // advance the position pszInputTxt += iDelimPos; if (*pszInputTxt == '\0') return pszInputTxt; else return pszInputTxt + 1; }
int main() { // Variables for diagnosis tree NodeT *root = NULL; // Variables for text List TextEntry text; TextList textList = newTextList(); // Variables for driver input control char szInputBuffer[MAX_LINE_SIZE+1]; // input command line int iScanfCnt; // sscanf return char *pszRemaining; // getToken returns this Token szCommand; // Input Command //Variables for reading data input command arguments Element element; // Binary Tree element char szParentId[MAX_ID_SIZE + 1]; // parent Id for NODE command char cYN; // Y or N value for NODE command char szAnswers[MAX_NUMBER_ANSWERS]; // Text for answers // Variables for results char *pszResultId; // Result of searchT or help // Read command lines until EOF while (fgets(szInputBuffer, MAX_LINE_SIZE, stdin) != NULL) { printf("%s", szInputBuffer); // If the line is just a comment, ignore it if (szInputBuffer[0] == '*') continue; // Command is a comment so skip it // get the command pszRemaining = getToken(szInputBuffer, szCommand, MAX_TOKEN_SIZE); // Determine what to invoke based on the command if (strcmp(szCommand, "ROOT") == 0) { // ROOT id - create the root for the tree pszRemaining = getToken(pszRemaining, element.szId, MAX_ID_SIZE); if (pszRemaining == NULL) ErrExit(ERR_DATA, "Invalid data for ROOT command, missing ID"); element.cNodeType = 'Q'; root = allocateNodeT(element); } else if (strcmp(szCommand, "NODE") == 0) { // NODE type id parentId yn iScanfCnt = sscanf(pszRemaining, "%c %s %s %c" , &element.cNodeType, element.szId , szParentId, &cYN); if (iScanfCnt < 4) ErrExit(ERR_DATA, "Invalid data for NODE command: '%s'", pszRemaining); // insert your code to check warning cases and handle the insertion insert(root, szParentId, element, cYN); } else if (strcmp(szCommand, "TEXT") == 0) { // TEXT type id displayText iScanfCnt = sscanf(pszRemaining, "%c %s %79[^\n]", &text.cType, text.szId, text.szText); if (iScanfCnt < 3) ErrExit(ERR_DATA, "Invalid data for TEXT command: '%s'", pszRemaining); addTextEntry(textList, text); } else if (strcmp(szCommand, "PRINT") == 0) { // PRINT using your prettyPrintT routine prettyPrintT(root, 0, textList); printf("\n\n"); } else if (strcmp(szCommand, "HELP") == 0) { // HELP id answers pszRemaining = getToken(pszRemaining, szAnswers, MAX_NUMBER_ANSWERS); if (pszRemaining == NULL) ErrExit(ERR_DATA, "Invalid data for HELP command, missing answers"); pszResultId = help(root, szAnswers, 0, textList); // It is expected that help might return NULL, if the tree is // not defined properly. (not necessarily a student error) if (pszResultId == NULL) printf("\t*** Warning: NULL returned from HELP\n"); else printf("\t%s: %s\n", pszResultId , getText(textList, pszResultId)); } else if (strcmp(szCommand, "DELETE") == 0) { pszRemaining = getToken(pszRemaining, element.szId, MAX_ID_SIZE); if (pszRemaining == NULL) ErrExit(ERR_DATA, "Invalid data for DELETE command, missing ID"); // insert your code for the DELETE command which should remove the // specified ID from its parent node and also free the subtree defined by // the specified ID deleteNode(root, element.szId); } } // Your code to free the tree freeT(root); // Free the textList free(textList); printf("\n"); return 0; }
/**************************** runSimulation ************************************ void runSimulation(Simulation sim, int iTimeLimit) Purpose: Goes through a list of events in a Simulation and run them as they are encountered. Parameters: I Simulation sim The simulation variable containing the list of events. I int iTimeLimit A time limit upon which the simulation will terminate if it is reached before all of the events are ran. Returns: Notes: *******************************************************************************/ void runSimulation(Simulation sim, int iTimeLimit) { //Variables Event event; Server server1, server2; Queue queue1, queue2; //create servers and queues server1 = newServer("Server 1"); queue1 = newQueue("Queue 1"); if (sim->cRunType == 'A') { server2 = newServer("Server 2"); queue2 = newQueue("Queue 2"); } //begin simulation printHeader(sim); while (removeLL(sim->eventList, &event)) { if (event.iTime > iTimeLimit) { printFooter(sim, queue1, queue2); freeServersAndQueues(sim, server1, server2, queue1, queue2); ErrExit(ERR_BAD_INPUT, "Event time (%d) is out of simulation bounds (%d)\n", event.iTime, iTimeLimit); } sim->iClock = event.iTime; switch (event.iEventType) { case EVT_ARRIVAL: arrival(sim, &event.widget); queueUp(sim, &event.widget, queue1); seize(sim, queue1, server1); break; case EVT_SERVER1_COMPLETE: release(sim, queue1, server1); if (sim->cRunType == 'A') //Alternative A follows up with server 2 { queueUp(sim, &event.widget, queue2); seize(sim, queue2, server2); } else //Alternative B and Current leave after server 1 { leaveSystem(sim, &event.widget); } break; case EVT_SERVER2_COMPLETE: release(sim, queue2, server2); leaveSystem(sim, &event.widget); break; default: ErrExit(ERR_ALGORITHM, "Unknown event type: %d\n", event.iEventType); } } printFooter(sim, queue1, queue2); freeServersAndQueues(sim, server1, server2, queue1, queue2); }
//-------------------------------------------------------------------------- // Parse the marker stream until SOS or EOI is seen; //-------------------------------------------------------------------------- static int ReadJpegSections (FILE * infile,ReadMode_t ReadMode) { int a; int HaveCom = FALSE; a = fgetc(infile); if (a != 0xff || fgetc(infile) != M_SOI){ return FALSE; } for(;SectionsRead < MAX_SECTIONS-1;){ int itemlen; int marker = 0; int ll,lh, got; uchar * Data; for (a=0;a<7;a++){ marker = fgetc(infile); if (marker != 0xff) break; if (a >= 6){ printf("too many padding bytes\n"); return FALSE; } } if (marker == 0xff){ // 0xff is legal padding, but if we get that many, something's wrong. ErrExit("too many padding bytes!"); } Sections[SectionsRead].Type = marker; // Read the length of the section. lh = fgetc(infile); ll = fgetc(infile); itemlen = (lh << 8) | ll; if (itemlen < 2){ ErrExit("invalid marker"); } Sections[SectionsRead].Size = itemlen; Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end. if (Data == NULL){ ErrExit("Could not allocate memory"); } Sections[SectionsRead].Data = Data; // Store first two pre-read bytes. Data[0] = (uchar)lh; Data[1] = (uchar)ll; got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section. if (got != itemlen-2){ ErrExit("reading from file"); } SectionsRead += 1; //printf("Marker '%x' size %d\n",marker, itemlen); switch(marker){ case M_SOS: // stop before hitting compressed data // If reading entire image is requested, read the rest of the data. if (ReadMode & READ_IMAGE){ int cp, ep, size; // Determine how much file is left. cp = ftell(infile); fseek(infile, 0, SEEK_END); ep = ftell(infile); fseek(infile, cp, SEEK_SET); size = ep-cp; Data = (uchar *)malloc(size); if (Data == NULL){ ErrExit("could not allocate data for entire image"); } got = fread(Data, 1, size, infile); if (got != size){ ErrExit("could not read the rest of the image"); } Sections[SectionsRead].Data = Data; Sections[SectionsRead].Size = size; Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; SectionsRead ++; HaveAll = 1; } return TRUE; case M_EOI: // in case it's a tables-only JPEG stream printf("No image in jpeg!\n"); return FALSE; case M_COM: // Comment section if (HaveCom || ((ReadMode & READ_EXIF) == 0)){ // Discard this section. free(Sections[--SectionsRead].Data); }else{ process_COM(Data, itemlen); HaveCom = TRUE; } break; case M_JFIF: // Regular jpegs always have this tag, exif images have the exif // marker instead, althogh ACDsee will write images with both markers. // this program will re-create this marker on absence of exif marker. free(Sections[--SectionsRead].Data); break; case M_EXIF: if (SectionsRead <= 2){ // Seen files from some 'U-lead' software with Vivitar scanner // that uses marker 31 later in the file (no clue what for!) process_EXIF((char *)Data, itemlen); }else{ // Discard this section. free(Sections[--SectionsRead].Data); } break; case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: process_SOFn(Data, marker); break; default: // Skip any other unknown sections. if (ShowTags){ printf("Unknown Jpeg section marker 0x%02x size %d\n",marker, itemlen); } break; } } return TRUE; }
//-------------------------------------------------------------------------- // Process one of the nested EXIF directories. //-------------------------------------------------------------------------- static void ProcessExifDir(char * DirStart, char * OffsetBase, unsigned ExifLength) { int de; int a; int NumDirEntries; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){ ErrExit("Illegally sized directory"); } if (ShowTags){ printf("Directory with %d entries\n",NumDirEntries); } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; char * ValuePtr; int ByteCount; char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrExit("Illegal format code in EXIF dir"); } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); ErrExit("Illegal pointer offset value in EXIF"); } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } if (LastExifRefd < ValuePtr+ByteCount){ // Keep track of last byte in the exif header that was actually referenced. // That way, we know where the discardable thumbnail data begins. LastExifRefd = ValuePtr+ByteCount; } if (ShowTags){ // Show tag name for (a=0;;a++){ if (TagTable[a].Tag == 0){ printf(" Unknown Tag %04x Value = ", Tag); break; } if (TagTable[a].Tag == Tag){ printf(" %s = ",TagTable[a].Desc); break; } } // Show tag value. switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) printf("\""); for (a=0;a<ByteCount;a++){ if (isprint((ValuePtr)[a])){ putchar((ValuePtr)[a]); } } printf("\"\n"); break; default: // Handle arrays of numbers later (will there ever be?) PrintFormatNumber(ValuePtr, Format); } } // Extract useful components of tag switch(Tag){ case TAG_MAKE: strncpy(ImageInfo.CameraMake, ValuePtr, 31); break; case TAG_MODEL: strncpy(ImageInfo.CameraModel, ValuePtr, 39); break; case TAG_DATETIME_ORIGINAL: strncpy(ImageInfo.DateTime, ValuePtr, 19); break; case TAG_USERCOMMENT: // Olympus has this padded with trailing spaces. Remove these first. for (a=ByteCount;;){ a--; if ((ValuePtr)[a] == ' '){ (ValuePtr)[a] = '\0'; }else{ break; } if (a == 0) break; } // Copy the comment if (memcmp(ValuePtr, "ASCII",5) == 0){ for (a=5;a<10;a++){ int c; c = (ValuePtr)[a]; if (c != '\0' && c != ' '){ strncpy(ImageInfo.Comments, a+ValuePtr, 199); break; } } }else{ strncpy(ImageInfo.Comments, ValuePtr, 199); } break; case TAG_FNUMBER: // Simplest way of expressing aperture, so I trust it the most. // (overwrite previously computd value if there is one) ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: // More relevant info always comes earlier, so only use this field if we don't // have appropriate aperture information yet. if (ImageInfo.ApertureFNumber == 0){ ImageInfo.ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); } break; case TAG_FOCALLENGTH: // Nice digital cameras actually save the focal length as a function // of how farthey are zoomed in. ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: // Inidcates the distacne the autofocus camera is focused to. // Tends to be less accurate as distance increases. ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: // Simplest way of expressing exposure time, so I trust it most. // (overwrite previously computd value if there is one) ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: // More complicated way of expressing exposure time, so only use // this value if we don't already have it from somewhere else. if (ImageInfo.ExposureTime == 0){ ImageInfo.ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); } break; case TAG_FLASH: if (ConvertAnyFormat(ValuePtr, Format)){ ImageInfo.FlashUsed = 1; } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: // Use largest of height and width to deal with images that have been // rotated to portrait format. a = (int)ConvertAnyFormat(ValuePtr, Format); if (ExifImageWidth < a) ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)){ case 1: FocalplaneUnits = 25.4; break; // inch case 2: // According to the information I was using, 2 means meters. // But looking at the Cannon powershot's files, inches is the only // sensible value. FocalplaneUnits = 25.4; break; case 3: FocalplaneUnits = 10; break; // centimeter case 4: FocalplaneUnits = 1; break; // milimeter case 5: FocalplaneUnits = .001; break; // micrometer } break; // Remaining cases contributed by: Volker C. Schoech ([email protected]) case TAG_EXPOSURE_BIAS: ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( ImageInfo.ISOequivalent < 80 ) ImageInfo.ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: ImageInfo.CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ ErrExit("Illegal subdirectory link"); } ProcessExifDir(SubdirStart, OffsetBase, ExifLength); continue; } } }