forsyth20121205-174063a80cc13 years agoHistory
504 lines · 9.7 KB · Brainfuck
RawDownload
implement Palm; ## Copyright © 2001-2003 Vita Nuova Holdings Limited.  All rights reserved.## Based on ``Palm® File Format Specification'', Document Number 3008-004, 1 May 2001, by Palm Inc.# Doc compression based on description by Paul Lucas, 18 August 1998# include "sys.m";	sys: Sys; include "daytime.m";	daytime: Daytime; include "palm.m"; # Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT"Epochdelta: con 2082844800;tzoff := 0; init(): string{	sys = load Sys Sys->PATH;	daytime = load Daytime Daytime->PATH;	if(daytime == nil)		return "can't load required module";	tzoff = daytime->local(0).tzoff;	return nil;} Record.new(id: int, attr: int, cat: int, size: int): ref Record{	return ref Record(id, attr, cat, array[size] of byte);} Resource.new(name: int, id: int, size: int): ref Resource{	return ref Resource(name, id, array[size] of byte);} Doc.open(m: Palmdb, file: ref Palmdb->PDB): (ref Doc, string){	info := m->file.db.stat();	if(info.dtype != "TEXt" || info.creator != "REAd")		return (nil, "not a Doc file: wrong type or creator");	r := m->file.read(0);	if(r == nil)		return (nil, sys->sprint("not a valid Doc file: %r"));	a := r.data;	if(len a < 16)		return (nil, sys->sprint("not a valid Doc file: bad length: %d", len a));	maxrec := m->file.db.nentries()-1;	d := ref Doc;	d.m = m;	d.file = file;	d.version = get2(a);	err := "unknown";	if(d.version != 1 && d.version != 2)		err = "unknown Docfile version";	# a[2:] is spare	d.length = get4(a[4:]);	d.nrec = get2(a[8:]);	if(maxrec >= 0 && d.nrec > maxrec){		d.nrec = maxrec;		err = "invalid record count";	}	d.recsize = get2(a[10:]);	d.position = get4(a[12:]);	return (d, sys->sprint("unexpected Doc file format: %s", err));} Doc.iscompressed(d: self ref Doc): int{	return (d.version&7) == 2;		# high-order bits are sometimes used, ignore them} Doc.read(doc: self ref Doc, index: int): (string, string){	m := doc.m;	DB, PDB: import m;	r := doc.file.read(index+1);	if(r == nil)		return (nil, sys->sprint("%r"));	(s, serr) := doc.unpacktext(r.data);	if(s == nil)		return (nil, serr);	return (s, nil);} Doc.unpacktext(doc: self ref Doc, a: array of byte): (string, string){	nb := len a;	s: string;	if(!doc.iscompressed()){		for(i := 0; i < nb; i++)			s[len s] = int a[i];	# assumes Latin-1		return (s, nil);	}	o := 0;	for(i := 0; i < nb;){		c := int a[i++];		if(c >= 9 && c <= 16r7F || c == 0)			s[o++] = c;		else if(c >= 1 && c <= 8){			if(i+c > nb)				return (nil, "missing data in record");			while(--c >= 0)				s[o++] = int a[i++];		}else if(c >= 16rC0 && c <= 16rFF){			s[o] = ' ';			s[o+1] = c & 16r7F;			o += 2;		}else{	# c >= 0x80 && c <= 16rBF			v := int a[i++];			m := ((c & 16r3F)<<5)|(v>>3);			n := (v&7) + 3;			if(m == 0 || m > o)				return (nil, sys->sprint("data is corrupt: m=%d n=%d o=%d", m, n, o));			for(; --n >= 0; o++)				s[o] = s[o-m];		}	}	return (s, nil);} Doc.textlength(doc: self ref Doc, a: array of byte): int{	nb := len a;	if(!doc.iscompressed())		return nb;	o := 0;	for(i := 0; i < nb;){		c := int a[i++];		if(c >= 9 && c <= 16r7F || c == 0)			o++;		else if(c >= 1 && c <= 8){			if(i+c > nb)				return -1;			o += c;			i += c;		}else if(c >= 16rC0 && c <= 16rFF){			o += 2;		}else{	# c >= 0x80 && c <= 16rBF			v := int a[i++];			m := ((c & 16r3F)<<5)|(v>>3);			n := (v&7) + 3;			if(m == 0 || m > o)				return -1;			o += n;		}	}	return o;} id2s(i: int): string{	if(i == 0)		return "";	return sys->sprint("%c%c%c%c", (i>>24)&16rFF, (i>>16)&16rFF, (i>>8)&16rFF, i&16rFF);} s2id(s: string): int{	n := 0;	for(i := 0; i < 4; i++){		c := 0;		if(i < len s)			c = s[i] & 16rFF;		n = (n<<8) | c;	}	return n;} DBInfo.new(name: string, attr: int, dtype: string, version: int, creator: string): ref DBInfo{	info := ref DBInfo;	info.name = name;	info.attr = attr;	info.version = version;	info.ctime = daytime->now();	info.mtime = daytime->now();	info.btime = 0;	info.modno = 0;	info.dtype = dtype;	info.creator = creator;	info.uidseed = 0;	info.index = 0;	return info;} Categories.new(labels: array of string): ref Categories{	c := ref Categories;	c.renamed = 0;	c.lastuid = 0;	c.labels = array[16] of string;	c.uids = array[] of {0 to 15 => 0};	for(i := 0; i < len labels && i < 16; i++){		c.labels[i] = labels[i];		c.lastuid = 16r80 + i;		c.uids[i] = c.lastuid;	}	return c;} Categories.unpack(a: array of byte): ref Categories{	if(len a < 16r114)		return nil;		# doesn't match the structure	c := ref Categories;	c.renamed = get2(a);	c.labels = array[16] of string;	c.uids = array[16] of int;	j := 2;	for(i := 0; i < 16; i++){		c.labels[i] = latin1(a[j:j+16], 0);		j += 16;		c.uids[i] = int a[16r102+i];	}	c.lastuid = int a[16r112];	# one byte of padding is shown on p. 26, but	# two more are invariably used in practice	# before application specific data.	if(len a > 16r116)		c.appdata = a[16r116:];	return c;} Categories.pack(c: self ref Categories): array of byte{	a := array[16r116 + len c.appdata] of byte;	put2(a, c.renamed);	j := 2;	for(i := 0; i < 16; i++){		puts(a[j:j+16], c.labels[i]);		j += 16;		a[16r102+i] = byte c.uids[i];	}	a[16r112] = byte c.lastuid;	a[16r113] = byte 0;	# pad shown on p. 26	a[16r114] = byte 0;	# extra two bytes of padding used in practice	a[16r115] = byte 0;	if(c.appdata != nil)		a[16r116:] = c.appdata;	return a;} Categories.mkidmap(c: self ref Categories): array of int{	a := array[256] of {* => 0};	for(i := 0; i < len c.uids; i++)		a[c.uids[i]] = i;	return a;} ## because PalmOS treats all times as local times, and doesn't associate# them with time zones, we'll convert using local time on Plan 9 and Inferno# pilot2epoch(t: int): int{	if(t == 0)		return 0;	# we'll assume it's not set	return t - Epochdelta + tzoff;} epoch2pilot(t: int): int{	if(t == 0)		return t;	return t - tzoff + Epochdelta;} ## map Palm name to string, assuming iso-8859-1,# but remap space and /#latin1(a: array of byte, remap: int): string{	s := "";	for(i := 0; i < len a; i++){		c := int a[i];		if(c == 0)			break;		if(remap){			if(c == ' ')				c = 16r00A0;	# unpaddable space			else if(c == '/')				c = 16r2215;	# division /		}		s[len s] = c;	}	return s;} ## map from Unicode to Palm name#filename(name: string): string{	s := "";	for(i := 0; i < len name; i++){		c := name[i];		if(c == ' ')			c = 16r00A0;	# unpaddable space		else if(c == '/')			c = 16r2215;	# division solidus		s[len s] = c;	}	return s;} dbname(name: string): string{	s := "";	for(i := 0; i < len name; i++){		c := name[i];		case c {		0 =>			c = ' ';	# unlikely, but just in case		16r2215 =>	c = '/';		16r00A0 =>	c = ' ';		}		s[len s] = c;	}	return s;} ## string conversion: can't use (string a) because# the bytes are Latin1, not Unicode#gets(a: array of byte): string{	s := "";	for(i := 0; i < len a; i++)		s[len s] = int a[i];	return s;} puts(a: array of byte, s: string){	for(i := 0; i < len a-1 && i < len s; i++)		a[i] = byte s[i];	for(; i < len a; i++)		a[i] = byte 0;} ##  big-endian packing# get4(p: array of byte): int{	return (((((int p[0] << 8) | int p[1]) << 8) | int p[2]) << 8) | int p[3];} get3(p: array of byte): int{	return (((int p[0] << 8) | int p[1]) << 8) | int p[2];} get2(p: array of byte): int{	return (int p[0]<<8) | int p[1];} put4(p: array of byte, v: int){	p[0] = byte (v>>24);	p[1] = byte (v>>16);	p[2] = byte (v>>8);	p[3] = byte (v & 16rFF);} put3(p: array of byte, v: int){	p[0] = byte (v>>16);	p[1] = byte (v>>8);	p[2] = byte (v & 16rFF);} put2(p: array of byte, v: int){	p[0] = byte (v>>8);	p[1] = byte (v & 16rFF);} ## DL protocol argument wrapping, based on conventions# extracted from include/Core/System/DLCommon.h in SDK 5## tiny arguments#	id: byte#	size: byte	# excluding this header#	data: byte[]## small arguments#	id: byte	# with 16r80 flag#	pad: byte#	size: byte[2]#	data: byte[]## long arguments#	id: byte	# with 16r40 flag#	pad: byte#	size: byte[4]#	data: byte[] # wrapper format flag in request/response argument IDShortWrap: con 16r80;	# 2-byte countLongWrap: con 16r40;	# 4-byte count Eshort: con "response shorter than expected"; ## set the system error string#e(s: string): string{	if(s != nil)		sys->werrstr(s);	return s;} argsize(args: array of (int, array of byte)): int{	totnb := 0;	for(i := 0; i < len args; i++){		(nil, a) := args[i];		n := len a;		if(n > 65535)			totnb += 6;	# long wrap		else if(n > 255)			totnb += 4;	# short		else			totnb += 2;	# tiny		totnb += n;	}	return totnb;} packargs(out: array of byte, args: array of (int, array of byte)): array of byte{	for(i := 0; i < len args; i++){		(id, a) := args[i];		n := len a;		if(n > 65535){			out[0] = byte (LongWrap|ShortWrap|id);			out[1] = byte 0;			put4(out[2:], n);			out = out[6:];		}else if(n > 255){			out[0] = byte (ShortWrap|id);			out[1] = byte 0;			put2(out[2:], n);			out = out[4:];		}else{			out[0] = byte id;			out[1] = byte n;			out = out[2:];		}		out[0:] = a;		out = out[n:];	}	return out;} unpackargs(argc: int, reply: array of byte): (array of (int, array of byte), string){	replies := array[argc] of (int, array of byte);	o := 0;	for(i := 0; i < len replies; i++){		o = (o+1)&~1;	# each argument starts at even offset		a := reply[o:];		if(len a < 2)			return (nil, e(Eshort));		rid := int a[0];		l: int;		if(rid & LongWrap){			if(len a < 6)				return (nil, e(Eshort));			l = get4(a[2:]);			a = a[6:];			o += 6;		}else if(rid & ShortWrap){			if(len a < 4)				return (nil, e(Eshort));			l = get2(a[2:]);			a = a[4:];			o += 4;		}else{			l = int a[1];			a = a[2:];			o += 2;		}		if(len a < l)			return (nil, e(Eshort));		replies[i] = (rid &~ 16rC0, a[0:l]);

Lines 1–500 of 504.