shithub: exif

Download patch

ref: ec5795bea63188b508fe1079174510072e7297cf
author: Alex Musolino <musolinoa@gmail.com>
date: Sun Nov 29 02:18:48 EST 2020

initial commit

--- /dev/null
+++ b/exif.c
@@ -1,0 +1,657 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+typedef struct IfdRec	IfdRec;
+typedef struct ExifData	ExifData;
+typedef struct EnumVal EnumVal;
+
+enum{
+	BigEndian,
+	LittleEndian,
+};
+
+enum{
+ 	ExifTag=0x8769,
+	GpsTag=0x8825,
+};
+
+enum{
+ 	ImageDescription=0x010e,
+ 	Make=0x010f,
+ 	Model=0x0110,
+ 	Orientation=0x0112,
+ 	XResolution=0x011a,
+ 	YResolution=0x011b,
+	ResolutionUnit=0x0128,
+ 	Software=0x0131,
+ 	DateTime=0x0132,
+	Artist=0x013b,
+ 	WhitePoint=0x013e,
+ 	PrimaryChromaticities=0x013f,
+ 	YCbCrCoefficients=0x0211,
+ 	YCbCrPositioning=0x0213,
+ 	ReferenceBlackWhite=0x0214,
+ 	Copyright=0x8298,
+	ExposureTime=0x829a,
+	FNumber=0x829d,
+	ExposureProgram=0x8822,
+	ISOSpeedRatings=0x8827,
+	SensitivityType=0x8830,
+	DateTimeOriginal=0x9003,
+	DateTimeDigitized=0x9004,
+	CompressedBitsPerPixel=0x9102,
+	ExposureBiasValue=0x9204,
+	MaxApertureValue=0x9205,
+	MeteringMode=0x9207,
+	LightSource=0x9208,
+	Flash=0x9209,
+	FocalLength=0x920a,
+	MakerNote=0x927c,
+	ColorSpace=0xa001,
+	PixelXDimension=0xa002,
+	PixelYDimension=0xa003,
+	InteroperabilityTag=0xa005,
+	SensingMethod=0xa217,
+	CustomRendered=0xa401,
+	ExposureMode,
+	WhiteBalance,
+	DigitalZoomRatio,
+	FocalLengthIn35mmFilm,
+	SceneCaptureType,
+	GainControl,
+	Contrast,
+	Saturation,
+	Sharpness,
+};
+
+enum{
+	Tbyte = 1,
+	Tascii = 2,
+	Tshort = 3,
+	Tlong = 4,
+	Tratio = 5,
+	Tundef = 7,
+	Tslong = 9,
+	Tsratio = 10,
+};
+
+static int prenum(IfdRec*, int, int, void*);
+static int prascii(IfdRec*, int, int, void*);
+static int prbyte(IfdRec*, int, Biobuf*, void*);
+static int prshort(IfdRec*, int, int, void*);
+static int prlong(IfdRec*, int, int, void*);
+static int prratio(IfdRec*, int, int, void*);
+static int prslong(IfdRec*, int, int, void*);
+static int prsratio(IfdRec*, int, int, void*);
+static int prdec(IfdRec*, int, int, void*);
+static int prundef(IfdRec*, int, int, void*);
+static int prifdrec(IfdRec*, int, Biobuf*);
+
+struct IfdRec
+{
+	u16int tag;
+	u16int fmt;
+	u32int cnt;
+	u8int val[4];
+	u32int ival;
+};
+
+struct ExifData
+{
+	u16int tag;
+	char str[32];
+	uchar show;
+	int (*print)(IfdRec*, int, int, void*);
+	void *aux;
+};
+
+struct EnumVal
+{
+	u32int val;
+	char *str;
+};
+
+int aflag, uflag, rflag;
+
+static EnumVal orientations[] = {
+	{ 1, "upper left" },
+	{ 3, "lower right" },
+	{ 6, "upper right" },
+	{ 8, "lower left" },
+	{ 9, "undefined" },
+	{ 0, nil }
+};
+
+static EnumVal resunits[] = {
+	{ 1, "no unit" },
+	{ 2, "inches" },
+	{ 3, "centimeters" },
+	{ 0, nil }
+};
+
+static EnumVal ycbcrpos[] = {
+	{ 1, "centered" },
+	{ 2, "co-sited" },
+	{ 0, nil }
+};
+
+static ExifData exiftab[] = {
+	{ Artist, "Artist", 0 },
+	{ DateTime, "DateTime", 1 },
+	{ ImageDescription, "ImageDescription", 1 },
+	{ Make, "Make", 1 },
+	{ Model, "Model", 1 },
+	{ Orientation, "Orientation", 0, prenum, orientations },
+	{ ResolutionUnit, "ResolutionUnit", 0, prenum, resunits },
+	{ Software, "Software", 1 },
+	{ XResolution, "XResolution", 0, prdec },
+	{ YResolution, "YResolution", 0, prdec },
+	{ YCbCrPositioning, "YCbCrPositioning", 0, prenum, ycbcrpos },
+	{ ExposureTime, "ExposureTime", 0, prdec, "s" },
+	{ FNumber, "FNumber", 0, prdec },
+	{ ExposureProgram, "ExposureProgram" },
+	{ ISOSpeedRatings, "ISOSpeedRatings" },
+	{ SensitivityType, "SensitivityType" },
+	{ DateTimeOriginal, "DateTimeOriginal" },
+	{ DateTimeDigitized, "DateTimeDigitized" },
+	{ CompressedBitsPerPixel, "CompressedBitsPerPixel", 0, prdec },
+	{ ExposureBiasValue, "ExposureBiasValue", 0, prdec },
+	{ MaxApertureValue, "MaxApertureValue", 0, prdec },
+	{ MeteringMode, "MeteringMode" },
+	{ LightSource, "LightSource" },
+	{ Flash, "Flash" },
+	{ FocalLength, "FocalLength", 0, prdec, "mm" },
+	{ ColorSpace, "ColorSpace" },
+	{ PixelXDimension, "PixelXDimension" },
+	{ PixelYDimension, "PixelYDimension" },
+	{ InteroperabilityTag, "InteroperabilityTag" },
+	{ SensingMethod, "SensingMethod" },
+	{ CustomRendered, "CustomRendered" },
+	{ ExposureMode, "ExposureMode" },
+	{ WhiteBalance, "WhiteBalance" },
+	{ DigitalZoomRatio, "DigitalZoomRatio", 0, prdec },
+	{ FocalLengthIn35mmFilm, "FocalLengthIn35mmFilm" },
+	{ SceneCaptureType, "SceneCaptureType" },
+	{ GainControl, "GainControl" },
+	{ Contrast, "Contrast" },
+	{ Saturation, "Saturation" },
+	{ Sharpness, "Sharpness" },
+
+	{ 0 }
+};
+
+static u16int
+get16(uchar *b, int e)
+{
+	if(e == BigEndian)
+		return (u16int)b[0]<<8 | b[1];
+	return (u16int)b[1]<<8 | b[0];
+}
+
+static int
+read16(Biobuf *r, int e, u16int *v)
+{
+	u8int b[2];
+
+	if(Bread(r, b, 2) != 2)
+		return -1;
+	if(v != nil)
+		*v = get16(b, e);
+	return 2;
+}
+
+static u32int
+get32(uchar *b, int bo)
+{
+	u32int h1, h2;
+
+	h1 = get16(&b[0], bo);
+	h2 = get16(&b[2], bo);
+	if(bo == BigEndian)
+		return h1<<16 | h2;
+	return h2<<16 | h1;
+}
+
+static int
+read32(Biobuf *r, int e, u32int *v)
+{
+	u8int b[4];
+
+	if(Bread(r, b, 4) != 4)
+		return -1;
+	if(v != nil)
+		*v = get32(b, e);
+	return 4;
+}
+
+static int
+prenum(IfdRec *r, int, int, void *aux)
+{
+	EnumVal *e;
+
+	e = aux;
+	if(r->cnt == 1){
+		while(e->str != nil){
+			if(e->val == r->ival)
+				return print("%s", e->str);
+			e++;
+		}
+	}
+	return print("unknown");
+}
+
+static char*
+fmtstr(u8int fmt)
+{
+	switch(fmt){
+	case Tbyte: return "byte";
+	case Tascii: return "ascii";
+	case Tshort: return "short";
+	case Tlong: return "long";
+	case Tratio: return "ratio";
+	case Tundef: return "undef";
+	case Tslong: return "slong";
+	case Tsratio: return "sratio";
+	}
+	return "inval";
+}
+
+static int
+prifdrec(IfdRec *r, int bo, Biobuf *b)
+{
+	ExifData *e;
+
+	for(e = exiftab; e->tag != 0; e++){
+		if(e->tag != r->tag)
+			continue;
+		break;
+	}
+	if(e->show || aflag){
+		if(*e->str != 0){
+			print("%s: ", e->str);
+		}else if(uflag)
+			print("unknown(0x%04uhx,%s): ", r->tag, fmtstr(r->fmt));
+		else
+			return 0;
+
+		if(rflag == 0 && e->print)
+			e->print(r, bo, b->fid, e->aux);
+		else
+			switch(r->fmt){
+			case Tbyte:
+				prbyte(r, bo, b, nil);
+				break;
+			case Tascii:
+				prascii(r, bo, b->fid, nil);
+				break;
+			case Tshort:
+				prshort(r, bo, b->fid, nil);
+				break;
+			case Tlong:
+				prlong(r, bo, b->fid, nil);
+				break;
+			case Tratio:
+				prratio(r, bo, b->fid, nil);
+				break;
+			case Tslong:
+				prslong(r, bo, b->fid, nil);
+				break;
+			case Tsratio:
+				prsratio(r, bo, b->fid, nil);
+				break;
+			case Tundef:
+				prundef(r, bo, b->fid, nil);
+				break;
+			}
+		print("\n");
+	}
+	return 0;
+}
+
+static int
+prlong0(IfdRec *r, int bo, int fd, void*, int s)
+{
+	int i;
+	uchar buf[4];
+	char *fmt;
+
+	fmt = s ? "%d" : "%ud";
+	switch(r->cnt){
+	case 0:
+		return 0;
+	case 1:
+		return print(fmt, get32(r->val, bo));
+	}
+	for(i = 0; i < r->cnt; i++){
+		if(pread(fd, buf, 2, r->ival + 12 + 4*i) < 0)
+			return -1;
+		print(fmt, get32(buf, bo));
+	}
+	return 0;
+}
+
+static int
+prslong(IfdRec *r, int bo, int fd, void *aux)
+{
+	return prlong0(r, bo, fd, aux, 1);
+}
+
+static int
+prlong(IfdRec *r, int bo, int fd, void *aux)
+{
+	return prlong0(r, bo, fd, aux, 0);
+}
+
+static int
+prshort(IfdRec *r, int bo, int fd, void*)
+{
+	int i;
+	uchar buf[2];
+
+	switch(r->cnt){
+	case 0:
+		return 0;
+	case 1:
+		return print("%uhd", get16(r->val, bo));
+	case 2:
+		return print("%uhd %uhd", get16(r->val, bo), get16(r->val+2, bo));
+	}
+	for(i = 0; i < r->cnt; i++){
+		if(pread(fd, buf, 2, r->ival + 12 + 2*i) < 0)
+			return -1;
+		print("%uhd", get16(buf, bo));
+	}
+	return 0;
+}
+
+static int
+prratio0(IfdRec *r, int bo, int fd, void*, int s)
+{
+	int i;
+	long n;
+	uchar buf[256];
+
+	n = sizeof(buf);
+	if(8*r->cnt < n)
+		n = 8*r->cnt;
+	if((n = pread(fd, buf, n, r->ival + 12)) < 0)
+		return -1;
+	for(i = 0; i < n; i += 8){
+		if(i > 0)
+			print(" ");
+		print(s ? "%d/%d" : "%ud/%ud", get32(&buf[i], bo), get32(&buf[i+4], bo));
+	}
+	return 0;
+}
+
+static int
+prsratio(IfdRec *r, int bo, int fd, void *aux)
+{
+	return prratio0(r, bo, fd, aux, 1);
+}
+
+static int
+prratio(IfdRec *r, int bo, int fd, void *aux)
+{
+	return prratio0(r, bo, fd, aux, 0);
+}
+
+static int
+prdec0(IfdRec *r, int bo, int fd, void *aux, int s)
+{
+	int i;
+	char *suffix;
+	uchar buf[8];
+	u32int n, d;
+	float f;
+
+	suffix = aux;
+	if(suffix == nil)
+		suffix = "";
+	for(i = 0; i < r->cnt; i++){
+		if(pread(fd, buf, 8, r->ival + 12 + 8*i) < 0)
+			return -1;
+		if(i > 0)
+			print(" ");
+		n = get32(&buf[i], bo);
+		d = get32(&buf[i+4], bo);
+		f = s ? (float)(long)n/(long)d : (float)n/d;
+		print("%g%s", f, suffix);
+	}
+	return 0;
+}
+
+static int
+prdec(IfdRec *r, int bo, int fd, void *aux)
+{
+	return prdec0(r, bo, fd, aux, r->fmt == Tsratio);
+}
+
+static int
+prbyte(IfdRec *r, int, Biobuf *b, void*)
+{
+	int c, i;
+
+	if(r->cnt <= 4)
+		return print("%c %c %c %c",
+			r->val[0], r->val[1], r->val[2], r->val[3]);
+	for(i = 0; i < r->cnt; i++){
+		if((c = Bgetc(b)) < 0)
+			return -1;
+		if(i > 0)
+			print(" ");
+		print("%c", (char)c);
+	}
+	return 0;
+}
+
+static int
+prascii(IfdRec *r, int, int fd, void*)
+{
+	long n;
+	char buf[256];
+
+	if(r->cnt <= 4){
+		if(r->cnt > 1)
+			return write(1, r->val, r->cnt - 1);
+		return 0;
+	}
+	n = sizeof(buf);
+	if(r->cnt < n)
+		n = r->cnt;
+	if((n = pread(fd, buf, n, r->ival + 12)) < 0)
+		return -1;
+	return write(1, buf, n);
+}
+
+static int
+prundef(IfdRec *r, int, int, void*)
+{
+	return print("cnt=%uhd val=(0x%uhhx 0x%uhhx 0x%uhhx 0x%uhhx)",
+		r->cnt, r->val[0], r->val[1], r->val[2], r->val[3]);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s file...\n", argv0);
+	exits("usage");
+}
+
+static int
+vparse(Biobuf *r, int e, char *fmt, va_list a)
+{
+	int c, i, n;
+	u8int *x;
+	u16int w;
+	u32int dw;
+
+	for(;;){
+		n = 0;
+		switch(*fmt++){
+		case '\0':
+			return 0;
+		case 'b':
+			if((c = Bgetc(r)) < 0)
+				return -1;
+			*va_arg(a, uchar*) = (uchar)c;
+			break;
+		case 'w':
+			if(read16(r, e, &w) < 0)
+				return -1;
+			*va_arg(a, u16int*) = w;
+			break;
+		case 'W':
+			if(read32(r, e, &dw) < 0)
+				return -1;
+			*va_arg(a, u32int*) = dw;
+			break;
+		case 'X':
+			n += 2;
+		case 'x':
+			n += 2;
+			x = va_arg(a, uchar*);
+			for(i = 0; i < n; i++){
+				if((c = Bgetc(r)) < 0)
+					return -1;
+				*x++ = (uchar)c;
+			}
+			break;
+		}
+	}
+}
+
+static int
+parse(Biobuf *r, int e, char *fmt, ...)
+{
+	int n;
+	va_list a;
+
+	va_start(a, fmt);
+	n = vparse(r, e, fmt, a);
+	va_end(a);
+	return n;
+}
+
+static int
+dumpinfo(Biobuf *r)
+{
+	char buf[64];
+	u16int w, soi, appn, len, ver, nents;
+	u32int ifdoff, exiftag, gpstag;
+	int i, bo, total;
+	IfdRec rec;
+
+	bo = BigEndian;
+	total = 0;
+	if(parse(r, bo, "www", &soi, &appn, &len) < 0)
+		goto Badfmt;
+	if(soi != 0xffd8 || (appn & 0xfff0) != 0xffe0)
+		goto Badfmt;
+	switch(appn & 0xf){
+	case 1:
+		break;
+	case 0:
+		fprint(2, "JFIF\n");
+	default:
+		goto Badfmt;
+	}
+	if(Bread(r, buf, 6) != 6 || strncmp(buf, "Exif", 6) != 0)
+		goto Badfmt;
+	if(read16(r, bo, &w) < 0)
+		goto Badfmt;
+	switch(w){
+	case 0x4949:
+		bo = LittleEndian;
+		break;
+	case 0x4d4d:
+		bo = BigEndian;
+		break;
+	default:
+		goto Badfmt;
+	}
+	if(parse(r, bo, "wW", &ver, &ifdoff) < 0)
+		goto Badfmt;
+	if(ver != 42 || ifdoff != 8)
+		goto Badfmt;
+	exiftag = 0;
+	gpstag = 0;
+	for(;;){
+		if(parse(r, bo, "w", &nents) < 0)
+			goto Badfmt;
+		for(i = 0; i < nents; i++){
+			memset(&rec, 0, sizeof(rec));
+			if(parse(r, bo, "wwWX", &rec.tag, &rec.fmt, &rec.cnt, rec.val) < 0)
+				goto Badfmt;
+			rec.ival = get32(rec.val, bo);
+			switch(rec.tag){
+			case ExifTag:
+				exiftag = rec.ival;
+				break;
+			case GpsTag:
+				gpstag = rec.ival;
+				break;
+			default:
+				prifdrec(&rec, bo, r);
+				break;
+			}
+			total++;
+		}
+		if(parse(r, bo, "w", &ifdoff) < 0)
+			goto Badfmt;
+		if(ifdoff == 0){
+			if(exiftag != 0){
+				ifdoff = exiftag;
+				exiftag = 0;
+			}else if(gpstag != 0){
+				ifdoff = gpstag;
+				gpstag = 0;
+			}else
+				break;
+		}
+		Bseek(r, ifdoff+12, 0);
+	}
+	return total;
+Badfmt:
+	werrstr("unknown file format");
+	return -1;
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+	Biobuf *r;
+
+	ARGBEGIN{
+	case 'a':
+		aflag++;
+		break;
+	case 'u':
+		uflag++;
+		break;
+	case 'r':
+		rflag++;
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(argc < 1)
+		usage();
+	for(i = 0; i < argc; i++){
+		r = Bopen(argv[i], OREAD);
+		if(r == nil)
+			sysfatal("open: %r");
+		if(i > 0)
+			print("\n");
+		print("File: %s\n", argv[i]);
+		switch(dumpinfo(r)){
+		case -1:
+			sysfatal("%r");
+		case 0:
+			fprint(2, "no exif data\n");
+		}
+		Bterm(r);
+	}
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+TARG=exif
+OFILES=\
+	exif.$O\
+
+</sys/src/cmd/mkone