Hello cyberpals.
Today we are going to take a detour from developing the IMAP
client and I want a diff tool
on System 6. I want to be able to
point it at the current
Carl directory and a recent backup and just have
it spit out a diff as if I were committing
it in CVS or Git or something.
Obviously we're not going to implement all of Git or anything crazy
like that. But I think just a diff tool that uses
like-- produces a modern unified diff with some
context and maybe like automatically recurse
or not even recurse, just do all the files
in two directories, should work because
I think like at the end of a video
or end of a session working on Carl, I would like
to just see what I've done.
What I've done so far is just-- this is
the OpenBSD diff source code
in usr.bin/diff.
The only thing that I've done is just convert Unix newlines
to Mac newlines and copy
them over through AppleShare. So I guess we'll get started.
So this is the-- like the diff command line
utility that parses all the command line arguments and stuff.
diffdir is for recursive diffing.
xmalloc is because diff uses xmalloc instead of malloc,
which is just a wrapper around malloc to abort if
malloc fails, which is just like the safe_malloc I'm using in Carl.
So I think the only code that we actually need to port is
this diffreg, which is the actual code that implements
the diff-- the diffing algorithm.
So we are going to create a new project
and call it... diff.
Because why not?
So we will put it in the diff directory and
call it... diff dot...
What happened there? What's going on?
That's weird. Why is Option+P not
producing a Pi symbol?
The other ones work, but not Option+P?
Okay, that's new to me.
Well, I guess we'll just call it diff.
How bizarre. Okay, and then we
are going to add diffreg.c
which does not show up because it is not
a file that THINK C knows about.
This is the annoying part of System 6.
Or, I guess the HFS filesystem
with the file type being stored
in the resource fork. Because if you copy
a file, it doesn't have any resource information
like these files that I just copied.
They don't have a file type and a program like THINK C
uses that file type to narrow down--
to filter the list of files in a directory that it can open.
So we need the text-- er, call these TEXT and KAHL?
That's weird.
So we need to copy that and
that way THINK C will see it.
That would be TEXT and KAHL, which is weird.
Okay, so now we should see diffreg.
We're also going to need xmalloc.
So there we go.
That is the code from usr.bin/diff.
I'm hoping we have most of those,
but we're also going to need
xmalloc and-- xmalloc.c and .h.
So we will--
We might need diff.h too.
I wonder why these have a creator of KAHL.
That's weird.
Or I should say, why THINK C creates files with that creator code.
Oh, hell, let's just do all the files
incase we need to look at them.
So that was diffdir too.
Okay. So now we should be
able to see them all in THINK C.
Yeah, so we need xmalloc.h and diff.h.
So we will add that to the project. And--
we can just add all these.
Well, we don't need these, we might
need to look at them, but we just want xmalloc.c
I'm very confused, why does Option+P not work.
It's very weird. Anyway.
So I guess we'll just compile this and see what
we got.
To set the project options to...
strict prototypes
just so that--
Okay. So let's just see where this goes.
So I'll just make
a main file that will
manually call-- we don't even have
a stat, and we don't have a wait.
I mean, HFS wouldn't have a stat call, so we'll need to fake up the--
err.h-- alright, we might want our own
error function that just fails.
Or probably raises a dialog instead, because we're not-- we don't
want this to be a command line program. We're going to--
I think before, I had seen
like, a diff program for Classic Mac OS
that was like a split screen.
And it wasn't like a unified diff and it just wasn't easy
to view the output out of.
It's much easier for me to view a unified diff and
understand it. And because this editor is
very bad about white space,
we'll have it ignore white space by default.
Oh, we need a regex engine? What the heck do we need that for?
Okay, so we don't need any of that for
like the diff command line utility.
When it's doing like
a recursive diff in a directory, you can--
Yeah, so we don't need any of this exclude stuff because
we're not going to be using it. These flags we're going to be passing ourselves
to diffreg.
Okay, so the flags
are passed as a bitmask, so that's good.
So we don't need that
and we don't have a stat struct.
Okay, so...
And we don't need excludes.
So then we're not going to need any of these at all.
What do we need that for?
So xasprintf mallocs
a buffer of that length and then does
an sprintf to it so we can--
we'll do that manually.
Huh. This is the original SSH code
before it became OpenSSH.
Well, I guess
we can just get away with that.
And I don't think we have file either or off_t.
So we'll do...
Do we have file?
Where is...
stdio. Like that Phil Collins song?
He was talking about the C
header file, you know?
Yeah, we have file.
So we probably just don't have off_t.
So we will put it in...
diff.h.
Right? Is that what it is?
Just tell me what it is, man.
Is it signed or unsigned?
Signed long.
Okay.
Is it the opposite?
Table for case folding. Yeah, we don't.
Okay.
character tran points to one of two translation
tables.
What is this for?
Oh, ignore case.
So.
Okay, so I guess we do need that.
For whitespace. I don't know.
[ mumbling ]
Maybe we should try not to change this
too much.
Ideally, we'd have some kind of very simple revision
control mechanism here instead of just copying directories
like I've been doing. But that is
a video for another day.
So I guess we did need that.
But what are they?
Okay. Okay, well,
I've already deleted that. So that was bad.
Let me.
How was it? vec?
Okay, so that was context_vec.
It is a struct.
Which is still there. What did I delete?
context_vec_ptr.
Okay, I guess I just deleted that.
So that was... [ narrating ]
Oh and we did need that chartran.
Yeah.
I should probably slow down. I'm not reading this
as closely as I should.
ISDIR...
Alright, we will need to do some
HFS code for that to see if
it is a directory or not.
And we don't have /dev/null.
Okie dokie
So we will not support diffing against
an empty file. Fine.
So we're going to have to reduce some of this, like, error checking.
And just assume that whatever files we get are regular files.
Alright, so I
think this is what we want.
I have to do that again.
Right. We don't have warn, we don't have err.
Okay, Let's put this in xmalloc.
So we need err
and then I think it takes a format string and then...
Okay,
I'm doing this in the Carl code.
So let me just take it
and raise_error and
then we'll like err just raise a dialog.
I think I'm just reserving a handle at
startup time to make
sure that it's always there.
Yeah.
I mean, this is kind of defeating the point because if we are erroring
out because we don't have any memory or something.
Alright.
And then we don't care about the return code
and then this
will be the same thing now.
This can just be warnx,
that's how it is in err.c
and then it just doesn't exit.
on path.
So this needs to take a va_list.
This is getting kind of stupid.
Right. So that's warnx.
[ narrating ]
Okay.
And then err will just call...
err is supposed to look up errno and
get the string value, but we don't have that here.
So we're just going to treat it like errx
which doesn't append the errno
because most of what we're calling this
function from isn't using errno anyway.
Then we also ExitToShell.
Right? Does that look right?
I don't care about the length
but I mean, this doesn't matter because we're exiting anyway.
This probably should care.
Okay, so that was more complicated
than it needed to be. And then we'll
put these in xmalloc.h.
Alright, so that was for
warnx.
And do we have errx?
Just err, warn, and warnx.
Yeah, whatever.
These all act the same. So we can
probably just say... what?
That won't work, right? Because you can't take all the arguments
and the C preprocessor is probably not very sophisticated
here. So whatever.
Are we even using warnx?
We are.
Yeah. Okay, so whatever.
Are we using errx too?
Okay, so we can get away with that.
Okay, let's try that again.
Prepare... what is prepare?
stb1. So we need the file size.
Because we're not calling stat on the...
on the file.
Okay.
So we will just hack around that and
Should we make a fake struct?
Would that be easier?
Sure.
Because.
Yeah, let's do that. And then we can
stop changing so much of this file because
I like to keep this file as compatible
as possible and then put all the other Mac stuff into a
different file.
Right. So we
need our new file.
So this file should probably not have all this stuff, but...
Okay, I'm not as worried about modifying this file.
And I'll say... [ narrating ]
Alright, so we need at least the
size.
So that would be-- where did we put
those others-- was that in diff.h?
We should probably not put those there either.
We'll put these in xmalloc.
Okay. And then st_size.
I need st_mode and st_mtime.
What is this ROR?
So it's kind of weird that diffreg references
things that are defined--
Alright, st_mode.
And what else do we need? mtime.
I guess that could just go in the include file.
We're not going to care about that.
We'll just say if the file size is not--
This is a C thing.
So we probably shouldn't have it.
UINT_PTR_MAX... so is that just--
long? unsigned long? Sure.
It's not the size, it's the actual value, right?
I don't know what that's for...
Since this is coming from OpenBSD where
int is probably of decent size.
Yeah, that's fine.
pread. What is preadline used for?
What the hell does pread do?
"pread and preadv
perform the same functions as read, but read from
the specified position offset in
the file without modifying the file pointer."
We don't have that, I guess.
So can we just read and then rewind?
You know what I mean?
Oh, wait, this is not--
these operate on file streams,
but we are using not that API.
How do you get the position
of a file handle that's not a file
object? lseek to--
what does lseek return?
Yeah. So I guess we can do [ reading ]
So we will read.
[ narrating code ]
And then seek back?
I think that's what I want, right?
Ah come on, we don't have lseek?
Yes, we do. It's in--
Right, we're not going to support this because
we don't have a regular expression engine,
so we're just going to...
comment it out.
How many lines is this file?
1484. So we're getting kind of far.
Oh I thought I wasn't using these flags.
What is the t flag?
"Print a tab rather than a space before the rest
of the line for the normal context."
We don't care about that.
Okay, cool.
Uh, what?
Oh, because it wants a const char.
Right, we don't have strlcpy
or strlcat.
I think we have strncpy and strncat.
Yeah. So we're gonna have to use strncpy
and strncat. Sorry, OpenBSD.
Did it work?
What is easprintf?
Why is this even in here?
Do we need emalloc too?
Okay, why are these in here?
Why doesn't this work?
Well, we're getting there.
These don't matter because we're not using errno.
So once all this compiles, I'm probably just going to have to
go through and change a lot of this stuff anyway, but right.
[ mumbling ]
Actually, I don't think we are using
reallocarray directly anywhere, so we can just fold
that into xreallocarray.
Yeah.
Okay.
Okay, we have no strdup.
Alright.
Why not?
What's the difference between vasprint and
vsprintf?
"vsprintf writes to the character string str
vasprintf writes to a dynamically allocated string that is stored in
ret". Right. So it
has to be allocated first.
We don't know how big it is.
Are we even using this?
Yes.
Are we using this?
We are not. That's neat.
See, we're cutting down our code already.
Cool.
That is a private API.
Okay.
All right. Can't be a const
because we're modifying it.
Right because we haven't made one.
Okay, so for now, I'm just going to print
because we're going to be using, like, a text console while we debug this
stuff anyway.
Okay.
Whatever.
I need to make a resource file and create an actual alert dialog.
My adapter finally came
today. It's a RJ11 jack on one end
and PS/2 on the other so you
can plug in a PS/2 keyboard into
your compact Mac so I can hack at
night on a quiet keyboard. But of course, I need
to find a quiet PS/2 keyboard, because the only one that I have
is an old Happy Hacking Keyboard, which is quite loud.
Oh, no.
Oh, I was like, wait how did it know all those prototypes then?
We just need to add the ANSI C library
and we should be good to go.
Donezo.
So these are probably, like, extern things
that were defined in diff.c that we're not using.
[ mumbling ]
We have to define some of these in main just to get it
to compile.
And in our main...
And then we'll have to call diffreg.
So it's nothing? Undefined?
Okay. Oh.
Creates a merged version of file one and file two on
the standard output. We don't
need any of that.
Whatever, we'll just say...
So I guess there's a lot of code in diffreg
that we can eventually yank out that we're not going to use because
I just care about unified diff format. Nothing fancy.
Okay,
Whatever. I just want to see it work, man.
fileno, lseek, and read. We don't have read?
I think we need... is it the unix?
As a separate library?
Yeah, I guess
this unix is another library we
need to add.
Oh, man, it ran. Cool.
Okay, so how do we call diffreg?
Okay, diffreg file1, file2, and flags.
That should be easy enough.
What do we want?
Macintosh.
carl
Actually, let's go from
the backup to the
current one. Obviously, this will have,
like, a GUI file picker when we're done.
And flags...
we will just pass, I don't know,
zero.
Okay,
ignore-- it's probably in diffreg something.
This would be really cool if it just works.
Oh, my God, it's working. Except it's not a unified diff.
It is an old school context diff.
Wow. That worked. That's so cool.
What is diff_format?
Oh, is this something we're supposed to set?
Yes, it is.
No. What is that? That is weird.
That doesn't seem right.
Does it? Oh, I think
it's because the amount of context
is wrong.
Yeah. What is diff_context set to
by default?
diff_context is three by default.
So we'll do that, too.
Cool.
Obviously in the final version we'll have to make our own
GUI window. Maybe with, like, a smaller
font. So that this is easier to--
Oh, that's cool.
That is perfect. That's what I wanted.
And then maybe we need to turn on the ignore
whitespace flag.
So the -w flag.
The flags for diffreg
are supposed to be compared to diff_format...
Okay. Maybe that's supposed to be in flags.
Okay.
So this is exactly what I wanted.
I'm going to just turn this into a GUI
program that draws its own window. And then... what?
Maybe, like, just two buttons to set the
source, er, the old and the new and
then spit out the diff
underneath. Something like that.
I don't know. But that was pretty
easy, just porting OpenBSD's
diff to MacOS System 6 with THINK C 5.
I guess I will play with that later
and then maybe use it in future videos to see what
I've done at the end of each video.
So, thanks for watching. Bye.