xref: /plan9/sys/src/cmd/ip/imap4d/imap4d.h (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * mailbox and message representations
37dd7cddfSDavid du Colombier  *
47dd7cddfSDavid du Colombier  * these structures are allocated with emalloc and must be explicitly freed
57dd7cddfSDavid du Colombier  */
67dd7cddfSDavid du Colombier typedef struct Box	Box;
77dd7cddfSDavid du Colombier typedef struct Header	Header;
87dd7cddfSDavid du Colombier typedef struct MAddr	MAddr;
97dd7cddfSDavid du Colombier typedef struct MbLock	MbLock;
107dd7cddfSDavid du Colombier typedef struct MimeHdr	MimeHdr;
117dd7cddfSDavid du Colombier typedef struct Msg	Msg;
127dd7cddfSDavid du Colombier typedef struct NamedInt	NamedInt;
137dd7cddfSDavid du Colombier typedef struct Pair	Pair;
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier enum
167dd7cddfSDavid du Colombier {
177dd7cddfSDavid du Colombier 	StrAlloc	= 32,		/* characters allocated at a time */
187dd7cddfSDavid du Colombier 	BufSize		= 8*1024,	/* size of transfer block */
197dd7cddfSDavid du Colombier 	NDigest		= 40,		/* length of digest string */
207dd7cddfSDavid du Colombier 	NUid		= 10,		/* length of .imp uid string */
217dd7cddfSDavid du Colombier 	NFlags		= 8,		/* length of .imp flag string */
227dd7cddfSDavid du Colombier 	LockSecs	= 5 * 60,	/* seconds to wait for acquiring a locked file */
23*9a747e4fSDavid du Colombier 	MboxNameLen	= 256,		/* max. length of upas/fs mbox name */
24*9a747e4fSDavid du Colombier 	MsgNameLen	= 32,		/* max. length of a file in a upas/fs mbox */
25*9a747e4fSDavid du Colombier 	UserNameLen	= 64,		/* max. length of user's name */
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier 	MUtf7Max	= 6,		/* max length for a modified utf7 character: &bbbb- */
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	/*
307dd7cddfSDavid du Colombier 	 * message flags
317dd7cddfSDavid du Colombier 	 */
327dd7cddfSDavid du Colombier 	MSeen		= 1 << 0,
337dd7cddfSDavid du Colombier 	MAnswered	= 1 << 1,
347dd7cddfSDavid du Colombier 	MFlagged	= 1 << 2,
357dd7cddfSDavid du Colombier 	MDeleted	= 1 << 3,
367dd7cddfSDavid du Colombier 	MDraft		= 1 << 4,
377dd7cddfSDavid du Colombier 	MRecent		= 1 << 5,
3880ee5cbfSDavid du Colombier 
3980ee5cbfSDavid du Colombier 	/*
4080ee5cbfSDavid du Colombier 	 * message bogus flags
4180ee5cbfSDavid du Colombier 	 */
4280ee5cbfSDavid du Colombier 	NotBogus	= 0,	/* the message is displayable */
4380ee5cbfSDavid du Colombier 	BogusHeader	= 1,	/* the header had bad characters */
4480ee5cbfSDavid du Colombier 	BogusBody	= 2,	/* the body had bad characters */
4580ee5cbfSDavid du Colombier 	BogusTried	= 4,	/* attempted to open the fake message */
467dd7cddfSDavid du Colombier };
477dd7cddfSDavid du Colombier 
487dd7cddfSDavid du Colombier struct Box
497dd7cddfSDavid du Colombier {
507dd7cddfSDavid du Colombier 	char	*name;		/* path name of mailbox */
517dd7cddfSDavid du Colombier 	char	*fs;		/* fs name of mailbox */
527dd7cddfSDavid du Colombier 	char	*fsDir;		/* /mail/fs/box->fs */
537dd7cddfSDavid du Colombier 	char	*imp;		/* path name of .imp file */
547dd7cddfSDavid du Colombier 	uchar	writable;	/* can write back messages? */
557dd7cddfSDavid du Colombier 	uchar	dirtyImp;	/* .imp file needs to be written? */
567dd7cddfSDavid du Colombier 	uchar	sendFlags;	/* need flags update */
577dd7cddfSDavid du Colombier 	Qid	qid;		/* qid of fs mailbox */
587dd7cddfSDavid du Colombier 	Qid	impQid;		/* qid of .imp when last synched */
597dd7cddfSDavid du Colombier 	long	mtime;		/* file mtime when last read */
607dd7cddfSDavid du Colombier 	ulong	max;		/* maximum msgs->seq, same as number of messages */
617dd7cddfSDavid du Colombier 	ulong	toldMax;	/* last value sent to client */
627dd7cddfSDavid du Colombier 	ulong	recent;		/* number of recently received messaged */
637dd7cddfSDavid du Colombier 	ulong	toldRecent;	/* last value sent to client */
647dd7cddfSDavid du Colombier 	ulong	uidnext;	/* next uid value assigned to a message */
657dd7cddfSDavid du Colombier 	ulong	uidvalidity;	/* uid of mailbox */
667dd7cddfSDavid du Colombier 	Msg	*msgs;
677dd7cddfSDavid du Colombier };
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier /*
707dd7cddfSDavid du Colombier  * fields of Msg->info
717dd7cddfSDavid du Colombier  */
727dd7cddfSDavid du Colombier enum
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier 	/*
757dd7cddfSDavid du Colombier 	 * read from upasfs
767dd7cddfSDavid du Colombier 	 */
777dd7cddfSDavid du Colombier 	IFrom,
787dd7cddfSDavid du Colombier 	ITo,
797dd7cddfSDavid du Colombier 	ICc,
807dd7cddfSDavid du Colombier 	IReplyTo,
817dd7cddfSDavid du Colombier 	IUnixDate,
827dd7cddfSDavid du Colombier 	ISubject,
837dd7cddfSDavid du Colombier 	IType,
847dd7cddfSDavid du Colombier 	IDisposition,
857dd7cddfSDavid du Colombier 	IFilename,
867dd7cddfSDavid du Colombier 	IDigest,
877dd7cddfSDavid du Colombier 	IBcc,
887dd7cddfSDavid du Colombier 	IInReplyTo,	/* aka internal date */
897dd7cddfSDavid du Colombier 	IDate,
907dd7cddfSDavid du Colombier 	ISender,
917dd7cddfSDavid du Colombier 	IMessageId,
927dd7cddfSDavid du Colombier 	ILines,		/* number of lines of raw body */
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier 	IMax
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier struct Header
987dd7cddfSDavid du Colombier {
997dd7cddfSDavid du Colombier 	char	*buf;		/* header, including terminating \r\n */
1007dd7cddfSDavid du Colombier 	ulong	size;		/* strlen(buf) */
1017dd7cddfSDavid du Colombier 	ulong	lines;		/* number of \n characters in buf */
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier 	/*
1047dd7cddfSDavid du Colombier 	 * pre-parsed mime headers
1057dd7cddfSDavid du Colombier 	 */
1067dd7cddfSDavid du Colombier 	MimeHdr	*type;		/* content-type */
1077dd7cddfSDavid du Colombier 	MimeHdr	*id;		/* content-id */
1087dd7cddfSDavid du Colombier 	MimeHdr	*description;	/* content-description */
1097dd7cddfSDavid du Colombier 	MimeHdr	*encoding;	/* content-transfer-encoding */
1107dd7cddfSDavid du Colombier 	MimeHdr	*md5;		/* content-md5 */
1117dd7cddfSDavid du Colombier 	MimeHdr	*disposition;	/* content-disposition */
1127dd7cddfSDavid du Colombier 	MimeHdr	*language;	/* content-language */
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier struct Msg
1167dd7cddfSDavid du Colombier {
1177dd7cddfSDavid du Colombier 	Msg	*next;
1187dd7cddfSDavid du Colombier 	Msg	*prev;
1197dd7cddfSDavid du Colombier 	Msg	*kids;
12080ee5cbfSDavid du Colombier 	Msg	*parent;
1217dd7cddfSDavid du Colombier 	char	*fsDir;		/* box->fsDir of enclosing message */
1227dd7cddfSDavid du Colombier 	Header	head;		/* message header */
1237dd7cddfSDavid du Colombier 	Header	mime;		/* mime header from enclosing multipart spec */
1247dd7cddfSDavid du Colombier 	int	flags;
1257dd7cddfSDavid du Colombier 	uchar	sendFlags;	/* flags value needs to be sent to client */
1267dd7cddfSDavid du Colombier 	uchar	expunged;	/* message actually expunged, but not yet reported to client */
1277dd7cddfSDavid du Colombier 	uchar	matched;	/* search succeeded? */
12880ee5cbfSDavid du Colombier 	uchar	bogus;		/* implies the message is invalid, ie contains nulls; see flags above */
1297dd7cddfSDavid du Colombier 	ulong	uid;		/* imap unique identifier */
1307dd7cddfSDavid du Colombier 	ulong	seq;		/* position in box; 1 is oldest */
1317dd7cddfSDavid du Colombier 	ulong	id;		/* number of message directory in upas/fs */
1327dd7cddfSDavid du Colombier 	char	*fs;		/* name of message directory */
1337dd7cddfSDavid du Colombier 	char	*efs;		/* pointer after / in fs; enough space for file name */
1347dd7cddfSDavid du Colombier 
13580ee5cbfSDavid du Colombier 	ulong	size;		/* size of fs/rawbody, in bytes, with \r added before \n */
1367dd7cddfSDavid du Colombier 	ulong	lines;		/* number of lines in rawbody */
1377dd7cddfSDavid du Colombier 
1387dd7cddfSDavid du Colombier 	char	*iBuf;
1397dd7cddfSDavid du Colombier 	char	*info[IMax];	/* all info about message */
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	char	*unixDate;
1427dd7cddfSDavid du Colombier 	MAddr	*unixFrom;
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	MAddr	*to;		/* parsed out address lines */
1457dd7cddfSDavid du Colombier 	MAddr	*from;
1467dd7cddfSDavid du Colombier 	MAddr	*replyTo;
1477dd7cddfSDavid du Colombier 	MAddr	*sender;
1487dd7cddfSDavid du Colombier 	MAddr	*cc;
1497dd7cddfSDavid du Colombier 	MAddr	*bcc;
1507dd7cddfSDavid du Colombier };
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier /*
1537dd7cddfSDavid du Colombier  * pre-parsed header lines
1547dd7cddfSDavid du Colombier  */
1557dd7cddfSDavid du Colombier struct MAddr
1567dd7cddfSDavid du Colombier {
1577dd7cddfSDavid du Colombier 	char	*personal;
1587dd7cddfSDavid du Colombier 	char	*box;
1597dd7cddfSDavid du Colombier 	char	*host;
1607dd7cddfSDavid du Colombier 	MAddr	*next;
1617dd7cddfSDavid du Colombier };
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier struct MimeHdr
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier 	char	*s;
1667dd7cddfSDavid du Colombier 	char	*t;
1677dd7cddfSDavid du Colombier 	MimeHdr	*next;
1687dd7cddfSDavid du Colombier };
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier /*
1717dd7cddfSDavid du Colombier  * mapping of integer & names
1727dd7cddfSDavid du Colombier  */
1737dd7cddfSDavid du Colombier struct NamedInt
1747dd7cddfSDavid du Colombier {
1757dd7cddfSDavid du Colombier 	char	*name;
1767dd7cddfSDavid du Colombier 	int	v;
1777dd7cddfSDavid du Colombier };
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier /*
1807dd7cddfSDavid du Colombier  * lock for all mail file operations
1817dd7cddfSDavid du Colombier  */
1827dd7cddfSDavid du Colombier struct MbLock
1837dd7cddfSDavid du Colombier {
1847dd7cddfSDavid du Colombier 	int	fd;
1857dd7cddfSDavid du Colombier };
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier /*
1887dd7cddfSDavid du Colombier  * parse nodes for imap4rev1 protocol
1897dd7cddfSDavid du Colombier  *
1907dd7cddfSDavid du Colombier  * important: all of these items are allocated
1917dd7cddfSDavid du Colombier  * in one can, so they can be tossed out at the same time.
1927dd7cddfSDavid du Colombier  * this allows leakless parse error recovery by simply tossing the can away.
1937dd7cddfSDavid du Colombier  * however, it means these structures cannot be mixed with the mailbox structures
1947dd7cddfSDavid du Colombier  */
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier typedef struct Fetch	Fetch;
1977dd7cddfSDavid du Colombier typedef struct NList	NList;
1987dd7cddfSDavid du Colombier typedef struct SList	SList;
1997dd7cddfSDavid du Colombier typedef struct MsgSet	MsgSet;
2007dd7cddfSDavid du Colombier typedef struct Store	Store;
2017dd7cddfSDavid du Colombier typedef struct Search	Search;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier /*
2047dd7cddfSDavid du Colombier  * parse tree for fetch command
2057dd7cddfSDavid du Colombier  */
2067dd7cddfSDavid du Colombier enum
2077dd7cddfSDavid du Colombier {
2087dd7cddfSDavid du Colombier 	FEnvelope,
2097dd7cddfSDavid du Colombier 	FFlags,
2107dd7cddfSDavid du Colombier 	FInternalDate,
2117dd7cddfSDavid du Colombier 	FRfc822,
2127dd7cddfSDavid du Colombier 	FRfc822Head,
2137dd7cddfSDavid du Colombier 	FRfc822Size,
2147dd7cddfSDavid du Colombier 	FRfc822Text,
2157dd7cddfSDavid du Colombier 	FBodyStruct,
2167dd7cddfSDavid du Colombier 	FUid,
2177dd7cddfSDavid du Colombier 	FBody,			/* BODY */
2187dd7cddfSDavid du Colombier 	FBodySect,		/* BODY [...] */
2197dd7cddfSDavid du Colombier 	FBodyPeek,
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	FMax
2227dd7cddfSDavid du Colombier };
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier enum
2257dd7cddfSDavid du Colombier {
2267dd7cddfSDavid du Colombier 	FPAll,
2277dd7cddfSDavid du Colombier 	FPHead,
2287dd7cddfSDavid du Colombier 	FPHeadFields,
2297dd7cddfSDavid du Colombier 	FPHeadFieldsNot,
2307dd7cddfSDavid du Colombier 	FPMime,
2317dd7cddfSDavid du Colombier 	FPText,
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 	FPMax
2347dd7cddfSDavid du Colombier };
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier struct Fetch
2377dd7cddfSDavid du Colombier {
2387dd7cddfSDavid du Colombier 	uchar	op;		/* F.* operator */
2397dd7cddfSDavid du Colombier 	uchar	part;		/* FP.* subpart for body[] & body.peek[]*/
2407dd7cddfSDavid du Colombier 	uchar	partial;	/* partial fetch? */
2417dd7cddfSDavid du Colombier 	long	start;		/* partial fetch amounts */
2427dd7cddfSDavid du Colombier 	long	size;
2437dd7cddfSDavid du Colombier 	NList	*sect;
2447dd7cddfSDavid du Colombier 	SList	*hdrs;
2457dd7cddfSDavid du Colombier 	Fetch	*next;
2467dd7cddfSDavid du Colombier };
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier /*
2497dd7cddfSDavid du Colombier  * status items
2507dd7cddfSDavid du Colombier  */
2517dd7cddfSDavid du Colombier enum{
2527dd7cddfSDavid du Colombier 	SMessages	= 1 << 0,
2537dd7cddfSDavid du Colombier 	SRecent		= 1 << 1,
2547dd7cddfSDavid du Colombier 	SUidNext	= 1 << 2,
2557dd7cddfSDavid du Colombier 	SUidValidity	= 1 << 3,
2567dd7cddfSDavid du Colombier 	SUnseen		= 1 << 4,
2577dd7cddfSDavid du Colombier };
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier /*
2607dd7cddfSDavid du Colombier  * parse tree for store command
2617dd7cddfSDavid du Colombier  */
2627dd7cddfSDavid du Colombier enum
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier 	STFlags,
2657dd7cddfSDavid du Colombier 	STFlagsSilent,
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 	STMax
2687dd7cddfSDavid du Colombier };
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier struct Store
2717dd7cddfSDavid du Colombier {
2727dd7cddfSDavid du Colombier 	uchar	sign;
2737dd7cddfSDavid du Colombier 	uchar	op;
2747dd7cddfSDavid du Colombier 	int	flags;
2757dd7cddfSDavid du Colombier };
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier /*
2787dd7cddfSDavid du Colombier  * parse tree for search command
2797dd7cddfSDavid du Colombier  */
2807dd7cddfSDavid du Colombier enum
2817dd7cddfSDavid du Colombier {
2827dd7cddfSDavid du Colombier 	SKNone,
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier 	SKCharset,
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 	SKAll,
2877dd7cddfSDavid du Colombier 	SKAnswered,
2887dd7cddfSDavid du Colombier 	SKBcc,
2897dd7cddfSDavid du Colombier 	SKBefore,
2907dd7cddfSDavid du Colombier 	SKBody,
2917dd7cddfSDavid du Colombier 	SKCc,
2927dd7cddfSDavid du Colombier 	SKDeleted,
2937dd7cddfSDavid du Colombier 	SKDraft,
2947dd7cddfSDavid du Colombier 	SKFlagged,
2957dd7cddfSDavid du Colombier 	SKFrom,
2967dd7cddfSDavid du Colombier 	SKHeader,
2977dd7cddfSDavid du Colombier 	SKKeyword,
2987dd7cddfSDavid du Colombier 	SKLarger,
2997dd7cddfSDavid du Colombier 	SKNew,
3007dd7cddfSDavid du Colombier 	SKNot,
3017dd7cddfSDavid du Colombier 	SKOld,
3027dd7cddfSDavid du Colombier 	SKOn,
3037dd7cddfSDavid du Colombier 	SKOr,
3047dd7cddfSDavid du Colombier 	SKRecent,
3057dd7cddfSDavid du Colombier 	SKSeen,
3067dd7cddfSDavid du Colombier 	SKSentBefore,
3077dd7cddfSDavid du Colombier 	SKSentOn,
3087dd7cddfSDavid du Colombier 	SKSentSince,
3097dd7cddfSDavid du Colombier 	SKSet,
3107dd7cddfSDavid du Colombier 	SKSince,
3117dd7cddfSDavid du Colombier 	SKSmaller,
3127dd7cddfSDavid du Colombier 	SKSubject,
3137dd7cddfSDavid du Colombier 	SKText,
3147dd7cddfSDavid du Colombier 	SKTo,
3157dd7cddfSDavid du Colombier 	SKUid,
3167dd7cddfSDavid du Colombier 	SKUnanswered,
3177dd7cddfSDavid du Colombier 	SKUndeleted,
3187dd7cddfSDavid du Colombier 	SKUndraft,
3197dd7cddfSDavid du Colombier 	SKUnflagged,
3207dd7cddfSDavid du Colombier 	SKUnkeyword,
3217dd7cddfSDavid du Colombier 	SKUnseen,
3227dd7cddfSDavid du Colombier 
3237dd7cddfSDavid du Colombier 	SKMax
3247dd7cddfSDavid du Colombier };
3257dd7cddfSDavid du Colombier 
3267dd7cddfSDavid du Colombier struct Search
3277dd7cddfSDavid du Colombier {
3287dd7cddfSDavid du Colombier 	int	key;
3297dd7cddfSDavid du Colombier 	char	*s;
3307dd7cddfSDavid du Colombier 	char	*hdr;
3317dd7cddfSDavid du Colombier 	ulong	num;
3327dd7cddfSDavid du Colombier 	int	year;
3337dd7cddfSDavid du Colombier 	int	mon;
3347dd7cddfSDavid du Colombier 	int	mday;
3357dd7cddfSDavid du Colombier 	MsgSet	*set;
3367dd7cddfSDavid du Colombier 	Search	*left;
3377dd7cddfSDavid du Colombier 	Search	*right;
3387dd7cddfSDavid du Colombier 	Search	*next;
3397dd7cddfSDavid du Colombier };
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier struct NList
3427dd7cddfSDavid du Colombier {
3437dd7cddfSDavid du Colombier 	ulong	n;
3447dd7cddfSDavid du Colombier 	NList	*next;
3457dd7cddfSDavid du Colombier };
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier struct SList
3487dd7cddfSDavid du Colombier {
3497dd7cddfSDavid du Colombier 	char	*s;
3507dd7cddfSDavid du Colombier 	SList	*next;
3517dd7cddfSDavid du Colombier };
3527dd7cddfSDavid du Colombier 
3537dd7cddfSDavid du Colombier struct MsgSet
3547dd7cddfSDavid du Colombier {
3557dd7cddfSDavid du Colombier 	ulong	from;
3567dd7cddfSDavid du Colombier 	ulong	to;
3577dd7cddfSDavid du Colombier 	MsgSet	*next;
3587dd7cddfSDavid du Colombier };
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier struct Pair
3617dd7cddfSDavid du Colombier {
3627dd7cddfSDavid du Colombier 	ulong	start;
3637dd7cddfSDavid du Colombier 	ulong	stop;
3647dd7cddfSDavid du Colombier };
3657dd7cddfSDavid du Colombier 
36680ee5cbfSDavid du Colombier #include "bin.h"
3677dd7cddfSDavid du Colombier 
36880ee5cbfSDavid du Colombier extern	Bin	*parseBin;
3697dd7cddfSDavid du Colombier extern	Biobuf	bout;
3707dd7cddfSDavid du Colombier extern	Biobuf	bin;
371*9a747e4fSDavid du Colombier extern	char	username[UserNameLen];
372*9a747e4fSDavid du Colombier extern	char	mboxDir[MboxNameLen];
3737dd7cddfSDavid du Colombier extern	char	*fetchPartNames[FPMax];
3747dd7cddfSDavid du Colombier extern	char	*site;
37580ee5cbfSDavid du Colombier extern	char	*remote;
376*9a747e4fSDavid du Colombier extern	int	debug;
3777dd7cddfSDavid du Colombier 
3787dd7cddfSDavid du Colombier #include "fns.h"
379