Commit message: None
CVE: CVE-2016-3822
CWE: CWE-119
Vulnerability description: exif.c in Matthias Wandel jhead 2.87, as used in libjhead in Android 4.x before 4.4.4, 5.0.x before 5.0.2, 5.1.x before 5.1.1, and 6.x before 2016-08-01, allows remote attackers to execute arbitrary code or cause a denial of service (out-of-bounds access) via crafted EXIF data, aka internal bug 28868315.

Project: Android

static void ConvertDoubleToURational(double value, unsigned int *numerator,
 unsigned int *denominator) {
    float2urat(value, 0xFFFFFFFFU, numerator, denominator);
}

Project: Android

int Exif2tm(struct tm * timeptr, char * ExifTime)
{
 int a;

    timeptr->tm_wday = -1;

    a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);


 if (a == 6){
        timeptr->tm_isdst = -1;
        timeptr->tm_mon -= 1; // Adjust for unix zero-based months
        timeptr->tm_year -= 1900; // Adjust for year starting at 1900
 return TRUE; // worked.
 }

 return FALSE; // Wasn't in Exif date format.
}

Project: Android

int Get32s(void * Long)
{
 if (MotorolaOrder){
 return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
 | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
 }else{
 return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
 | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
 }
}

Project: Android

unsigned Get32u(void * Long)
{
 return (unsigned)Get32s(Long) & 0xffffffff;
}

Project: Android

int IsDateTimeTag(unsigned short tag)
{
 return ((tag == TAG_DATETIME)? TRUE: FALSE);
}

Project: Android

void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
{
 int s,n;

 for(n=0;n<16;n++){
 switch(Format){
 case FMT_SBYTE:
 case FMT_BYTE:      printf("%02x",*(uchar *)ValuePtr); s=1; break;
 case FMT_USHORT:    printf("%d",Get16u(ValuePtr)); s=2; break;
 case FMT_ULONG:
 case FMT_SLONG:     printf("%d",Get32s(ValuePtr)); s=4; break;
 case FMT_SSHORT:    printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
 case FMT_URATIONAL:
 case FMT_SRATIONAL:
               printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
               s = 8;
 break;

 case FMT_SINGLE:    printf("%f",(double)*(float *)ValuePtr); s=8; break;
 case FMT_DOUBLE:    printf("%f",*(double *)ValuePtr);        s=8; break;
 default:
                printf("Unknown format %d:", Format);
 return;
 }
 ByteCount -= s;
 if (ByteCount <= 0) break;
        printf(", ");
 ValuePtr = (void *)((char *)ValuePtr + s);

 }
 if (n >= 16) printf("...");
}

Project: Android

static void Put16u(void * Short, unsigned short PutValue)
{
 if (MotorolaOrder){
 ((uchar *)Short)[0] = (uchar)(PutValue>>8);
 ((uchar *)Short)[1] = (uchar)PutValue;
 }else{
 ((uchar *)Short)[0] = (uchar)PutValue;
 ((uchar *)Short)[1] = (uchar)(PutValue>>8);
 }
}

Project: Android

void ShowConciseImageInfo(void)
{
    printf("\"%s\"",ImageInfo.FileName);

    printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);

 if (ImageInfo.ExposureTime){
 if (ImageInfo.ExposureTime <= 0.5){
            printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
 }else{
            printf(" (%1.1f)",ImageInfo.ExposureTime);
 }
 }

 if (ImageInfo.ApertureFNumber){
        printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
 }

 if (ImageInfo.FocalLength35mmEquiv){
        printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
 }

 if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
        printf(" (flash)");
 }

 if (ImageInfo.IsColor == 0){
        printf(" (bw)");
 }

    printf("\n");
}

Project: Android

void ShowImageInfo(int ShowFileInfo)
{
 if (ShowFileInfo){
        printf("File name    : %s\n",ImageInfo.FileName);
        printf("File size    : %d bytes\n",ImageInfo.FileSize);

 {
 char Temp[20];
 FileTimeAsString(Temp);
            printf("File date    : %s\n",Temp);
 }
 }

 if (ImageInfo.CameraMake[0]){
        printf("Camera make  : %s\n",ImageInfo.CameraMake);
        printf("Camera model : %s\n",ImageInfo.CameraModel);
 }
 if (ImageInfo.DateTime[0]){
        printf("Date/Time    : %s\n",ImageInfo.DateTime);
 }
    printf("Resolution   : %d x %d\n",ImageInfo.Width, ImageInfo.Height);

 if (ImageInfo.Orientation > 1){
        printf("Orientation  : %s\n", OrientTab[ImageInfo.Orientation]);
 }

 if (ImageInfo.IsColor == 0){
        printf("Color/bw     : Black and white\n");
 }

 if (ImageInfo.FlashUsed >= 0){
 if (ImageInfo.FlashUsed & 1){
            printf("Flash used   : Yes");
 switch (ImageInfo.FlashUsed){
 case 0x5: printf(" (Strobe light not detected)"); break;
 case 0x7: printf(" (Strobe light detected) "); break;
 case 0x9: printf(" (manual)"); break;
 case 0xd: printf(" (manual, return light not detected)"); break;
 case 0xf: printf(" (manual, return light  detected)"); break;
 case 0x19:printf(" (auto)"); break;
 case 0x1d:printf(" (auto, return light not detected)"); break;
 case 0x1f:printf(" (auto, return light detected)"); break;
 case 0x41:printf(" (red eye reduction mode)"); break;
 case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
 case 0x47:printf(" (red eye reduction mode return light  detected)"); break;
 case 0x49:printf(" (manual, red eye reduction mode)"); break;
 case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
 case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
 case 0x59:printf(" (auto, red eye reduction mode)"); break;
 case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
 case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
 }
 }else{
            printf("Flash used   : No");
 switch (ImageInfo.FlashUsed){
 case 0x18:printf(" (auto)"); break;
 }
 }
        printf("\n");
 }


 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
        printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
 if (ImageInfo.FocalLength35mmEquiv){
            printf("  (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
 }
        printf("\n");
 }

 if (ImageInfo.DigitalZoomRatio > 1){
        printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
 }

 if (ImageInfo.CCDWidth){
        printf("CCD width    : %4.2fmm\n",(double)ImageInfo.CCDWidth);
 }

 if (ImageInfo.ExposureTime){
 if (ImageInfo.ExposureTime < 0.010){
            printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
 }else{
            printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
 }
 if (ImageInfo.ExposureTime <= 0.5){
            printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
 }
        printf("\n");
 }
 if (ImageInfo.ApertureFNumber){
        printf("Aperture     : f/%3.3f\n",(double)ImageInfo.ApertureFNumber);
 }
 if (ImageInfo.Distance){
 if (ImageInfo.Distance < 0){
            printf("Focus dist.  : Infinite\n");
 }else{
            printf("Focus dist.  : %4.2fm\n",(double)ImageInfo.Distance);
 }
 }

 if (ImageInfo.ISOequivalent){
        printf("ISO equiv.   : %2d\n",(int)ImageInfo.ISOequivalent);
 }

 if (ImageInfo.ExposureBias){
        printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
 }

 switch(ImageInfo.Whitebalance) {
 case 1:
            printf("Whitebalance : Manual\n");
 break;
 case 0:
            printf("Whitebalance : Auto\n");
 break;
 }

 switch(ImageInfo.LightSource) {
 case 1:
            printf("Light Source : Daylight\n");
 break;
 case 2:
            printf("Light Source : Fluorescent\n");
 break;
 case 3:
            printf("Light Source : Incandescent\n");
 break;
 case 4:
            printf("Light Source : Flash\n");
 break;
 case 9:
            printf("Light Source : Fine weather\n");
 break;
 case 11:
            printf("Light Source : Shade\n");
 break;
 default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
 }

 if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
 switch(ImageInfo.MeteringMode) {
 case 2:
            printf("Metering Mode: center weight\n");
 break;
 case 3:
            printf("Metering Mode: spot\n");
 break;
 case 5:
            printf("Metering Mode: matrix\n");
 break;
 }
 }

 if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
 switch(ImageInfo.ExposureProgram) {
 case 1:
            printf("Exposure     : Manual\n");
 break;
 case 2:
            printf("Exposure     : program (auto)\n");
 break;
 case 3:
            printf("Exposure     : aperture priority (semi-auto)\n");
 break;
 case 4:
            printf("Exposure     : shutter priority (semi-auto)\n");
 break;
 case 5:
            printf("Exposure     : Creative Program (based towards depth of field)\n");
 break;
 case 6:
            printf("Exposure     : Action program (based towards fast shutter speed)\n");
 break;
 case 7:
            printf("Exposure     : Portrait Mode\n");
 break;
 case 8:
            printf("Exposure     : LandscapeMode \n");
 break;
 default:
 break;
 }
 }
 switch(ImageInfo.ExposureMode){
 case 0: // Automatic (not worth cluttering up output for)
 break;
 case 1: printf("Exposure Mode: Manual\n");
 break;
 case 2: printf("Exposure Mode: Auto bracketing\n");
 break;
 }

 if (ImageInfo.DistanceRange) {
        printf("Focus range  : ");
 switch(ImageInfo.DistanceRange) {
 case 1:
                printf("macro");
 break;
 case 2:
                printf("close");
 break;
 case 3:
                printf("distant");
 break;
 }
        printf("\n");
 }



 if (ImageInfo.Process != M_SOF0){
 int a;
 for (a=0;;a++){
 if (a >= (int)PROCESS_TABLE_SIZE){
                printf("Jpeg process : Unknown\n");
 break;
 }
 if (ProcessTable[a].Tag == ImageInfo.Process){
                printf("Jpeg process : %s\n",ProcessTable[a].Desc);
 break;
 }
 }
 }

 if (ImageInfo.GpsInfoPresent){
        printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
        printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
 if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
 }

 if (ImageInfo.Comments[0]){
 int a,c;
        printf("Comment      : ");
 if (!ImageInfo.CommentWidchars){
 for (a=0;a<MAX_COMMENT_SIZE;a++){
                c = ImageInfo.Comments[a];
 if (c == '\0') break;
 if (c == '\n'){
 if (ImageInfo.Comments[a+1] != '\0'){
                        printf("\nComment      : ");
 }else{
                        printf("\n");
 }
 }else{
                    putchar(c);
 }
 }
            printf("\n");
 }else{
            printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
 }
 }
 if (ImageInfo.ThumbnailOffset){
        printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
 } else {
        printf("NO thumbnail");
 }
}

Project: Android

int TagNameToValue(const char* tagName)
{
 unsigned int i;
 for (i = 0; i < TAG_TABLE_SIZE; i++) {
 if (strcmp(TagTable[i].Desc, tagName) == 0) {
            printf("found tag %s val %d", TagTable[i].Desc, TagTable[i].Tag);
 return TagTable[i].Tag;
 }
 }
    printf("tag %s NOT FOUND", tagName);
 return -1;
}

Project: Android

static const TagTable_t* TagToTagTableEntry(unsigned short tag)
{
 unsigned int i;
 for (i = 0; i < TAG_TABLE_SIZE; i++) {
 if (TagTable[i].Tag == tag) {
            printf("found tag %d", tag);
 int format = TagTable[i].Format;
 if (format == 0) {
                printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc);
 return NULL;
 }
 return &TagTable[i];
 }
 }
    printf("tag %d NOT FOUND", tag);
 return NULL;
}

Project: Android

void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag)
{
 const int EXIF_MAX_SIZE = 1024*64;
 char* Buffer = malloc(EXIF_MAX_SIZE);

 if (Buffer != NULL) {
        create_EXIF_internal(elements, exifTagCount, gpsTagCount, hasDateTimeTag, Buffer);
        free(Buffer);
 } else {
 ErrFatal("Could not allocate memory");
 }
}

Project: Android

static void create_EXIF_internal(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag, char* Buffer)
{
 unsigned short NumEntries;
 int DataWriteIndex;
 int DirIndex;
 int DirExifLink = 0;

#ifdef SUPERDEBUG
    ALOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
#endif

 MotorolaOrder = 0;

    memcpy(Buffer+2, "Exif\0\0II",8);
 Put16u(Buffer+10, 0x2a);

 DataWriteIndex = 16;
 Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset.  Means start 16 bytes in.

 {
 DirIndex = DataWriteIndex;
 NumEntries = 1 + exifTagCount; // the extra is the thumbnail
 if (gpsTagCount) {
 ++NumEntries; // allow for the GPS info tag
 }
 if (!hasDateTimeTag) {
 ++NumEntries;
 }
 DataWriteIndex += 2 + NumEntries*12 + 4;

 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
 DirIndex += 2;

 if (!hasDateTimeTag) {
 char* dateTime = NULL;
 char dateBuf[20];
 if (ImageInfo.numDateTimeTags) {
                dateTime = ImageInfo.DateTime;
 } else {
 FileTimeAsString(dateBuf);
                dateTime = dateBuf;
 }
            writeExifTagAndData(TAG_DATETIME,
                                FMT_STRING,
 20,
 (long)(char*)dateBuf,
                                FALSE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);

 }
 if (exifTagCount > 0) {
 int i;
 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
 if (elements[i].GpsTag) {
 continue;
 }
 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
 if (entry == NULL) {
 continue;
 }
#ifdef SUPERDEBUG
                ALOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
#endif
                writeExifTagAndData(elements[i].Tag,
                                    entry->Format,
                                    entry->DataLength,
 (long)elements[i].Value,
                                    TRUE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }

 if (gpsTagCount) {
                writeExifTagAndData(TAG_GPSINFO,
                                    FMT_ULONG,
 1,
 DataWriteIndex-8,
                                    FALSE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }

 int exifDirPtr = DataWriteIndex-8;
 if (gpsTagCount) {
                exifDirPtr += 2 + gpsTagCount*12 + 4;
 }
 DirExifLink = DirIndex;
            writeExifTagAndData(TAG_EXIF_OFFSET,
                                FMT_ULONG,
 1,
                                exifDirPtr,
                                FALSE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }

 Put32u(Buffer+DirIndex, 0);
        printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
 }


 if (gpsTagCount) {
 DirIndex = DataWriteIndex;
        printf("Starting GPS section DirIndex = %d", DirIndex);
 NumEntries = gpsTagCount;
 DataWriteIndex += 2 + NumEntries*12 + 4;

 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
 DirIndex += 2;
 {
 int i;
 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
 if (!elements[i].GpsTag) {
 continue;
 }
 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
 if (entry == NULL) {
 continue;
 }
#ifdef SUPERDEBUG
                ALOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
#endif
                writeExifTagAndData(elements[i].Tag,
                                    entry->Format,
                                    entry->DataLength,
 (long)elements[i].Value,
                                    TRUE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }
 }

 Put32u(Buffer+DirIndex, 0);
        printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
 }

 {
 Put32u(Buffer+DirExifLink+8, DataWriteIndex-8);

        printf("Starting Thumbnail section DirIndex = %d", DirIndex);
 DirIndex = DataWriteIndex;
 NumEntries = 2;
 DataWriteIndex += 2 + NumEntries*12 + 4;

 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
 DirIndex += 2;
 {
            writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
                                FMT_ULONG,
 1,
 DataWriteIndex-8,
                                FALSE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }

 {
            writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
                                FMT_ULONG,
 1,
 0,
                                FALSE,
 Buffer,
 &DirIndex,
 &DataWriteIndex);
 }

 Put32u(Buffer+DirIndex, 0);
        printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
 }


 Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
 Buffer[1] = (unsigned char)DataWriteIndex;

 RemoveSectionType(M_EXIF);

 {
 unsigned char * NewBuf = malloc(DataWriteIndex);
 if (NewBuf == NULL){
 ErrFatal("Could not allocate memory");
 }
        memcpy(NewBuf, Buffer, DataWriteIndex);

 CreateSection(M_EXIF, NewBuf, DataWriteIndex);

        process_EXIF(NewBuf, DataWriteIndex);
 }
}

Project: Android

static void float2urat(double value, unsigned int max, unsigned int *numerator,
 unsigned int *denominator) {
 if (value <= 0) {
 *numerator = 0;
 *denominator = 1;
 return;
 }

 if (value > max) {
 *numerator = max;
 *denominator = 1;
 return;
 }

 if (value < 1e-9) {
 unsigned int n = (unsigned int)(value * max);
 if (n == 0) {
 *numerator = 0;
 *denominator = 1;
 } else {
 *numerator = n;
 *denominator = max;
 }
 return;
 }

 unsigned int d;
 for (d = 1000000000; d >= 1; d /= 10) {
 double s = value * d;
 if (s <= max) {
 unsigned int n = (unsigned int)s;
 while (n % 10 == 0 && d >= 10) {
                n /= 10;
                d /= 10;
 }
 *numerator = n;
 *denominator = d;
 return;
 }
 }

 *numerator = 0;
 *denominator = 1;
}

Project: Android

void process_EXIF (unsigned char * ExifSection, unsigned int length)
{
 int FirstOffset;

 FocalplaneXRes = 0;
 FocalplaneUnits = 0;
 ExifImageWidth = 0;
 NumOrientations = 0;

 if (ShowTags){
        printf("Exif header %d bytes long\n",length);
 }

 { // Check the EXIF header component
 static uchar ExifHeader[] = "Exif\0\0";
 if (memcmp(ExifSection+2, ExifHeader,6)){
 ErrNonfatal("Incorrect Exif header",0,0);
 return;
 }
 }

 if (memcmp(ExifSection+8,"II",2) == 0){
 if (ShowTags) printf("Exif section in Intel order\n");
 MotorolaOrder = 0;
 }else{
 if (memcmp(ExifSection+8,"MM",2) == 0){
 if (ShowTags) printf("Exif section in Motorola order\n");
 MotorolaOrder = 1;
 }else{
 ErrNonfatal("Invalid Exif alignment marker.",0,0);
 return;
 }
 }

 if (Get16u(ExifSection+10) != 0x2a){
 ErrNonfatal("Invalid Exif start (1)",0,0);
 return;
 }

 FirstOffset = Get32u(ExifSection+12);
 if (FirstOffset < 8 || FirstOffset > 16){
 ErrNonfatal("Suspicious offset of first IFD value",0,0);
 return;
 }

 DirWithThumbnailPtrs = NULL;


 ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);

 ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
#ifdef SUPERDEBUG
    printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
#endif
 if (DumpExifMap){
 unsigned a,b;
        printf("Map: %05d- End of exif\n",length-8);
 for (a = 0; a < length - 8; ++a) {
 unsigned char c = *(ExifSection+8+a);
 unsigned pc = isprint(c) ? c : ' ';
            printf("Map: %4d %02x %c", a, c, pc);
 }
 }


 if (FocalplaneXRes != 0){
 ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);

 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0
 && ImageInfo.FocalLength35mmEquiv == 0){
 ImageInfo.FocalLength35mmEquiv = (int)(
 (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom
 / ImageInfo.CCDWidth * 36 + 0.5);
 }
 }
}

Project: Android

static void writeExifTagAndData(int tag,
 int format,
 long components,
 long value,
 int valueInString,
 char* Buffer,
 int* DirIndex,
 int* DataWriteIndex) {
 void* componentsPosition = NULL; // for saving component position

 Put16u(Buffer+ (*DirIndex), tag); // Tag
 Put16u(Buffer+(*DirIndex) + 2, format); // Format
 if (format == FMT_STRING && components == -1) {
        components = strlen((char*)value) + 1; // account for null terminator
 if (components & 1) ++components; // no odd lengths
 } else if (format == FMT_SSHORT && components == -1) {
        components = 1;
 }
 if (format == FMT_UNDEFINED && components == -1) {
 if(memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0) {
            components = sizeof(ExifAsciiPrefix) +
                         strlen((char*)value + sizeof(ExifAsciiPrefix)) + 1;
 if (components & 1) ++components; // no odd lengths
 }
 }
 Put32u(Buffer+(*DirIndex) + 4, components); // Components
    componentsPosition = Buffer+(*DirIndex) + 4; // components # can change for lists
    printf("# components: %ld", components);
 if (format == FMT_STRING) {
 if (components <= 4) {
            strcpy(Buffer+(*DirIndex) + 8, (char*)value);
 } else {
 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
            printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
            strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
 (*DataWriteIndex) += components;
 }
 } else if ((format == FMT_UNDEFINED) &&
 (memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0)) {
 if (components <= 4) {
            memcpy(Buffer+(*DirIndex) + 8, (char*)value, components);
 } else {
 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
            printf("copying %s to %d", (char*)value + sizeof(ExifAsciiPrefix), (*DataWriteIndex));
            memcpy(Buffer+(*DataWriteIndex), (char*)value, components);
 (*DataWriteIndex) += components;
 }
 } else if (!valueInString) {
 Put32u(Buffer+(*DirIndex) + 8, value); // Value
 } else {
 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
 char* curElement = strtok((char*)value, ",:");
 int i;

 for (i = 0; ((i < components) || (components == -1)) && curElement != NULL; i++) {
#ifdef SUPERDEBUG
            printf("processing component %s format %s", curElement, formatStr(format));
#endif
 if (format == FMT_URATIONAL) {
 unsigned int numerator;
 unsigned int denominator;
 char* separator = strchr(curElement, '/');
 if (separator) {
                    numerator = atoi(curElement);
                    denominator = atoi(separator + 1);
 } else {
 double value = atof(curElement);
 ConvertDoubleToURational(value, &numerator, &denominator);
 }
 Put32u(Buffer+(*DataWriteIndex), numerator);
 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
 (*DataWriteIndex) += 8;
 } else if (format == FMT_SRATIONAL) {
 int numerator;
 int denominator;
 char* separator = strchr(curElement, '/');
 if (separator) {
                    numerator = atoi(curElement);
                    denominator = atoi(separator + 1);
 } else {
 double value = atof(curElement);
 ConvertDoubleToSRational(value, &numerator, &denominator);
 }
 Put32u(Buffer+(*DataWriteIndex), numerator);
 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
 (*DataWriteIndex) += 8;
 } else if ((components == -1) && ((format == FMT_USHORT) || (format == FMT_SSHORT))) {
                value = atoi(curElement);
 Put16u(Buffer+(*DataWriteIndex), value);
 (*DataWriteIndex) += 4;
 } else {
                value = atoi(curElement);
 Put32u(Buffer+(*DirIndex) + 8, value); // Value
 }
            curElement = strtok(NULL, ",:");
 }
 if (components == -1) Put32u(componentsPosition, i); // update component # for unknowns
 }
 (*DirIndex) += 12;
}