Hello, cyberpals.
In the previous episode, we used the LDEF
code resource trick thing to simplify
our list definition for the message list.
And I ran into a weird issue with
LGetCell not being able to find
the imap_message struct that we were passing from LSetCell
and I did some more debugging off camera,
and I realized that what I needed to do was--
apparently LSetCell is expecting
a pointer to a pointer. So that's why
things were not coming out correctly on the opposite end.
Here, I just added this to pass it as
a reference. Even though this is already a pointer. So I guess now
it's passing a pointer to a pointer. And once
I did that in the LSetCell, when we come
back out in message list and do
LGetCell, we are now able to find
it without having to directly tap into the
imap_open_mailbox message list.
So that's good. Because this is kind of the more correct way to do.
And so I fixed that and then I also just applied the
same pattern to the mailbox list.
And so now, obviously this C file
is in the main project instead of in its own mailbox
LDEF project. And this is just changed from main to
mailbox_list_ldef. And it does the
same thing, it's doing LGetCell, pulls up the mailbox and then
just prints the name in the cell. So now everything is
working correctly for the lists and I wanted to draw
the message list a bit differently to include
the date. But we were not saving the date
out of the envelope command
to the IMAP server.
And then as I thought about it, I realized. Well, how are
you going to show a date anyway?
Because they're all going to be in different time zones and there's
no--there's no concept of time zones in System 6.
So the system clock is just in your
local time zone, there's no function in the toolbox
for converting into time zones, there's no time zone database.
So the way that I thought I would proceed is
to make-- add another setting, I guess,
that would be like in the preferences window, along with the default
mailbox. And some of these other things. And like the IMAP hostname
and username and stuff, that would basically just you
have to put in your time zone offset in the
format of like, that's
the local offset for Chicago where I am.
And so if you just put that in the
preferences it could pull that out, parse that as your five hours
behind UTC, and then the
dates and times that it gets
from the envelope command are in a standard format,
which is defined in RFC 822 I believe,
one of the old ones for SMTP.
So I wrote this little utility function.
Kind of over the past two days here and there that
basically can just take a string
like this and this is what all of the dates and times that
we'll get back from the IMAP server will be but not in the actual
message contents. This is like what the IMAP server has parsed out and
returned. So this function will
take-- this parse822time function--
will take a string like this and then
parse it out with sscanf. And then basically
run mktime on it.
Because as we're parsing it out we're
sticking it into a struct tm.
And that is,
if I bring it up here, so that's
the standard struct that's used on
the Unix or C library, I guess, time functions,
so we're basically pulling all the values
out of this string with sscanf
putting it into this struct, we have to do some fix ups like
change the year, we have to convert a string
like "Fri", find it in our
array and then return 6 or
5 and I left these as full
length strings even though they're going to be three bytes
because I don't know, maybe we'll have to parse them later but,
so it tries to find the month as
an integer, the day of the week as an integer.
and then it calls mktime, which converts
this struct into a time_t,
which is unsigned long, and on System 6,
the epoch is different than UNIX.
So it's a much smaller integer value.
But then it's going to try
to convert the time zone offset of like this.
And then take that as
five hours behind. And then change
this time to add
five hours.
And then basically give you this time in UTC.
And then when we want to display this time,
we would take the time_t value and then
convert it into our local time zone, which is
going to be pulled out of the preferences,
and then it would add
or in this case subtract, that many hours from the
time_t value and give you a time, more
or less accurately, in your local time zone.
Obviously it's not accounting for, like,
leap seconds and Daylight Saving Time
like, you'll have to manually update that to be negative
five or negative six or whatever. So anyway, I think
that should be enough just to kind of give you a rough I
mean, fairly accurate day and time.
If it's like a second off or something or
even, like, one hour off it's not a huge deal because it's
only going to be used for, like, showing the message list.
So I think that should work enough for now. So,
like I said, I already wrote this and
then we just need to plug it into the existing
code, which is in the IMAP parser.
Because we're pulling
out the date and time from the envelope but we're not doing anything with it.
So that would be imap_get_word.
So we are pulling
out the date and time.
And so we are going to store that as a time_t in
the imap_message struct
and we'll just call this time_t.
And so now we can--
it's parsed as a string.
We can call parse822time and word,
and the size, and we'll get back a time_t.
[ narrating ]
So that would be the time in UTC and
that's how we're going to store it. And then we display it later,
we will show it with the local offset because
I think, at some point in the future, all of these
message and mailbox structs are going to get paged out
to cache on the disk. So that we don't have to
pull them in or fetch them over the Internet
each time.
So let us see if that works first.
And then once we know that the date is there we can display
it in some form in the message list.
I mentioned previously that I had created a channel on
Freenode called #cyberpals, there's now a few
of us in there talking about classic Mac development
and helping each other out. So if you're
following along or if you're just interested in this kind of stuff
join Freenode in the #cyberpals channel
and ask your questions.
I should note that I did have
to change from the ANSI small library to the full ANSI C
library because some of the time functions that I needed
were not in the small library. So that's
kind of unfortunate. But, I guess this program is
getting bigger, it needs more stuff anyway.
Alright, and let me just set a breakpoint where
we parse the date so that
we can see that it's working.
Okay.
So now we have a string.
Oops, I mean word.
So that's what we're going to parse and then this should return
a time of 19
at a time_t.
So sscanf...
We will do...
We could probably just skip to the end because
I'm pretty sure this stuff works.
And then if we look at the struct,
everything should be there. Seconds, minutes, hours.
Day of the month, month, year, minus 1900.
And then off should be -500
And then we will convert -500 to seconds.
And now message->date should be now be a...
so it's an unsigned
long, but the debugger is confused.
Okay, so that
seems to work correctly.
Same for this. So we
did not account for that. Okay.
I guess we need a static
list of those time zones.
In the RFC822 document
it only gives a short list of--
it only gives the American time zones,
which is kind of funny.
Okay, so we will do-- how
to we do this...
so we have just EST, EDT.
But what is the return before?
That might be some more work then, I'm going to have to get a list of
the time zone abbreviations and then create like a
big array of all of them and
their corresponding time zone offset. So I guess
for now,
what to do so... maybe we will
store this as just a string and then
parse it into an integer.
And if that parsing fails just treat
it as zero for now.
And then we will say...
[ loudly typing ]
Maybe we can get away with that. And parse it with atoi.
Otherwise we'll need to do our lookup table.
Yeah, that might work.
Alright.
What?
Isn't atoi in... oh it's in stdlib, where all the STD's live.
Alright, then we'll just break in here because we know that
the other part works in the IMAP code.
So we just care about parsing at this point.
Okey dokey.
Okay, I just want to make
sure that it's not trying to parse this leading zero as
like, an octal thing.
Okay.
So that works.
And now we have +200
which should give us 200
and now we have -6..
Oh, what did I see before? That's weird.
I could have sworn I saw something that was not numbers.
Am I crazy?
Unless the sscanf fails
and I'm not even getting here.
That would be bad.
Let's try that, too.
Am I crazy or was there a message in there that has a--
now I'm going to have to go back and review this video anyway.
So I guess that's working now and we'll hit that breakpoint if
it comes back.
Very bizarre because the messages don't change in
the message list or from link the response from the server. Anyway.
So now that we have the date as a time_t,
we can show it in the list,
but we need to add it as a preference.
So again we're just going to hard-code
to this in the resource file. And then later we'll make
a preferences window that allows all of these things to be changed.
I'll create a new resource
and call it...
Is it a string or data?
The string.
Okay, so the string will say...
That's not what I wanted.
We will rename it.
I am now regretting starting these at 1000, I don't know why I did that.
I guess we could...
yeah, those are IMAP-specific settings,
they can be a different range and we'll use the default here and
we'll just call this LOCAL_TZOFFSET.
Okay, so that is 128.
Wait are all these are the same--
That's troubling.
Right, so each resource has to be a unique ID. But I
guess I put these in the 1,000 range because I wanted
to keep them all grouped.
So let us call this something else.
And then picture is
in the 1000 range, it should not be.
Let's put it in the 500 range.
Okie dokie. And then we'll update the about box.
That window id is...
That's weird, why does it give 128?
I am suspicious now.
These are all in the same namespace, right?
[ whispering ] I don't know.
Alright, so our
new string for that preference is 2000 and
we will kind of initialize that.
Yeah, these are all the same. So I think it's
per resource-- it's per resource type. Because when you do
GetResource it's like, GetResource LDEF and then the ID.
Yeah that's fine, the picture can
be 128 as well because
it's a PICT type. So they're
per-type, but then, like in the strings I'm going
to still group them by like, what kind of thing they are.
Okay let's make the seat here on ourselves. Bye,
Okie dokie.
And then our strings...
Well, yeah, these can stay as there.
Okay, so now we can say [ reading ]
Then we will convert this at startup time
because it can be used by multiple things.
The other thing that I learned as I came
across something in a PDF
book that I have, which is Macintosh C Programming by Example
published in 1991 and it's, like a pretty
decent C programming book, but it's using
THINK C and it says to never use
the int type in a Mac program. And the reason for
that is, per project,
you can define whether an int is 2 or 4 bytes.
Hello.
Somewhere aroud here.
Here, 4 byte ints or 8 byte ints-- er, no, 2 or 4, sorry.
And because you can set this per project it's like,
not portable to just say int in your code.
So you should pretty much just always use short or long whether you want two
bytes or four bytes.
And so I have not been following that because I didn't know about it.
So, like all of these things where we pass, these should be size_t anyway,
but basically anywhere that I'm using int, I should probably update
them and be more precise whether I want a short
or long. I think in most cases,
I was assuming it was two bytes. Because I'm
not really using long unless I would
have specified long. So probably all the ints can just
become shorts. So anyway, we'll say local_tzoff
is a long. And then we
will parse this at startup
time and say
I can't really imagine why we would need it
this early, but what the heck.
So we need to fetch this as a string first and
then do atoi on it.
Yeah that's good enough for us.
Alright, so we can say...
and then we'll say
[ narrating ]
Okay, so I guess we should
move this down because that might raise an error and
we should probably have a menu bar if we do that.
Okay, so the local_tzoff is atoi on tzoff_s.
You can just pass that directly to tzoff_secs. Sex?
And then anywhere we need local_tzoff,
it will already be in seconds.
So let's just see if that works.
Previously I asked about these strings
that are not character raise because they're in single
quotes and it's basically
the same as if you did like one character
in single quotes it's the
integer value of that string-- er of that character.
And then if you have multiple, just make them
as bytes. So,
like, if you have a four
character, if you have four characters in the single quotes it
turns into a long integer value of those
four bytes.
So the GetResource function must
be like searching for
that long value rather
than like, comparing it as a string. So that
makes sense because
there wouldn't be like a master list of resources that
you would compare as strings. So just having it as an ID makes sense.
local_tzoffset ID
And we need [ mumbling ]
Why are you compiling tcp again? That makes no sense.
Oh, that might be pulling in util.h which I think I changed.
We'll just default it to zero because that's good
for a time zone offset.
So now local_tzoff should be 5
hours worth of seconds or negative 5.
And that seems right. Okay, so that works.
Now we can just jump into showing...
Showing the date of each message and the message list
so that would be in the message list function
er, file. So I
wanted it to be to match my mutt config which is a two
digit month and two
digit day.
All these are going to be in a variable width font.
So we have the date in a GMT
time_t and we want to convert that to a local time_t
with our local offset. And then
we want to use
strftime
I want
a strftime to print the month and
date, which should
be available.
In time.c.
strftime takes a target, max,
format, and
a tm struct.
Maybe we can just do it ourselves since we
have to convert it to a tm anyway.
And how does one do that?
I like in a file
that the function to convert to GMT just
"nope, I don't know what time zones are".
How do you get a struct tm
from a time_t?
localtime? This is
using a toolbox function. Okay.
Okie dokie.
So that would be a struct tm.
What the heck was that outside?
Oh, that returns the address
of a static struct. Cool, cool.
So we don't have to free it.
We can't free it, I should say. Okay.
So tm_month
plus 1 and then m_day.
It's probably easier to just strncpy that.
Okay, and then do the usual shenanigans
of printing it. And then we will...
let's make that guy responsible
for moving the cursor
because it knows how wide it just printed.
Alright, I don't know,
So let's see if that works.
I probably need time in here.
I was just typing in the #cyberpals
channel so you should join it.
localtime imessage
was I wrong?
Because we're directly calling localtime.
mon? Hey, mon.
So if we are doing this right,
these messages should show a date,
month and day of May [ what? ]
And then let's just step
in here just in case we go off
the rails.
So again, the first one should be 9/18 the 9/26 and 9/30.
[ mumbling ]
Okay, so this should be 9/18.
And it is! Huzzah.
Except we should probably pad these.
Ah, we'll do that next time.
Awesome. Whoops, I want to swap these two.
So this should be 9/26.
It be.
And then 9/30.
Cool. So it looks like our time shenanigans
are working correctly.
So that's neat.
Okay, I guess we just need to pad that. And then maybe
move-- swap these two and
that should be good to go for the message list.
At least the formatting.
Although, if the message is new, it would be cool to print it
all in bold.
Okay, so that's fine. I don't think
we needed 40.
Then we will move the subject.
That's more important to me than who
said it.
And then we will give that
a wide berth.
Girth? Berth. Wide berth.
Okie dokie. So we can just run
that and see what it looks like.
That seems to be too much.
Can we... no, we can't.
If we could just modify it in place.
I don't know.
See that's why I wanted to put this in its own file,
because these little tweaks, we won't have to recompile the whole program,
These might overlap with a long subject. So I
may need to erase the rect around
it first. And then-- ah, come on, man.
You were doing so well.
Go anyway. No? Great.
That's disappointing.
Did we do something weird?
No.
Alright.
Alright, so we'll need to
erase this rect to trim
the string first, then maybe we'll add more
space there.
Okay. So we
will do...
We'll say textRect.left... 5
[ narrating ]
and then draw the text bigger.
This is why I have-- the there's like some
ifdef IMAP stub code in the imap.c
file where it would basically just not connect
and then kind of like hard code a list of things so that testing
was quicker. So it didn't have to connect and login and
download the list and do all that stuff just to
show what the layout is. But I don't think
I got it gets as far as like showing the message list.
So I could probably rip that out now because we already know the rest
of this layout is fine.
That did not do I wanted.
Also I think this is off by one, that's too high.
Okay.
Well, anyway, you get the idea.
I'm going to fix this. I think I need to maybe set the rect
dot right to here if
it's not already, which no, I think it is.
Alright I'll need to figure out why that's not erasing properly and then maybe
move this over just a smidge and then it should
be good to go. So thanks for watching I'm
not sure, maybe we'll do that bold new messages
in the next video or if you want to see something
else, let me know otherwise I'll
probably do the new message count in the
message list and show the new count over here.
Kind of like right aligned. But anyway, thanks for
watching. If you are interested, join the #cyberpals
channel on Freenode otherwise
subscribe to my site's RSS feed for more videos. Thanks.