1,816 karma · joined December 17, 2010
The trickier bit would be to find one of those old SCSI-attached ethernet devices (Asanté, DaynaPort, EtherMac, ...) since they're coveted items in the retro scene. Although it looks like at least PiSCSI can emulate a DaynaPort https://github.com/PiSCSI/piscsi/wiki/Dayna-Port-SCSI-Link
Was expecting it to be higher, actually.
Apple apparently felt comfortable enough with the eSIM situation in the US to drop legacy-SIM already. The fact that they couldn't do this worldwide yet is because, as you say, eSIM availability is still spotty around the world.
However, the fact that they're moving to eSIM-only is a huge flashing warning to telcos around the world that the time to sort out their eSIM support is now. Otherwise, they'll soon be chasing away their affluent iPhone users.
I think the transition will now proceed pretty quickly. Physical SIMs have only continued on due to market inertia.
In a few years we'll look back and think "remember when SIM cards were physical objects... that was weird wasn't it?"
(Also available as "man ascii" from almost as long ago https://github.com/dspinellis/unix-history-repo/blob/Researc... )
OpenBSD seems to like collecting extra items though
SF is certainly a city with its problems. I can't say that wasn't part of my decision to move away years ago.
However, the current right-wing meme of "new left-wing prosecutors instantly turned SF into an unliveable hellscape" is mostly just well-financed propaganda. The statistics don't support a huge surge in violent crime except in the sense that (like everywhere) it's rebounded from temporary lows during covid lockdowns: https://twitter.com/petercalloway/status/1510032517876764674
You always have to read the inode to determine whether the file is executable. The inode included the locations of the first "direct" blocks of the file's contents. So if the file is empty you know that immediately.
If the file even had a single comment line, it would mean having to do a disk seek to read it. So it would effectively double the disk I/O needed to run "/bin/true"
Thus why the (per-machine, read-write) /var became standardized. Earlier /usr contained a mix of writable things (like /usr/log). Now it was important to make /usr something that could be NFS mounted read-only by a bunch of NFS clients.
There were also UNIXes that ran on multiple architectures, like SunOS on 68k and SPARC. In theory, you could have them both use the same read-only /usr/share mount and save some resources on the server.
I say "in theory" because I don't know how many sites ever implemented this in practice. There were definitely places that supported mixed-architecture diskless fleets, but I'm unsure how many of them were committed to keeping the OS version pinned between them... or how many then went through the extra work to make /usr/share a separate mountpoint. I'm sure some people did it, but it's not something I remember seeing personally.
Still, I feel it's somewhat useful to help humans understand what parts of a package are CPU-specific and which aren't. Probably good that it's lived on.
And, yes, it exists even in the earliest versions of the "cal" command, dating back to the mid 1970s: https://github.com/dspinellis/unix-history-repo/blob/Researc...
The implementation is pretty clever as well. The jan1() function simply returns the day-of-week the year starts on (and knows about the calendar-system change). The cal() function compares jan1(y) and jan1(y+1) to detect leap years by noticing if the weekday of Jan 1st advances one or two days.. and if it's anything else it assumes it's handling the 1752 case. Then inside of the printing loop it just jumps from 2 to 14 whenever it is rendering a month that happens to contain 19 days.
The very first C compilers ran on a PDP-11 in just a few dozen kilobytes of memory. The entire emphasis was on minimalism, and that meant that things like type enforcement was left to the human.
One of the things the earliest language was missing was the "void" type. If you didn't give a return type to a function, it just defaulted to returning "int". Therefore it was totally normal to have a function fail to return anything.
Since there was no distinction between "function returning int" and "function returning nothing", there was nothing stopping your program from using the value returned... it just meant you got whatever happened to be in the right register.
What does it mean for us today? Basically nothing. "void" was added 30+ years ago when ANSI C appeared. The language couldn't just break the old behavior but in any rational environment you enable enough compiler warnings to avoid these ancient quirks entirely:
$ gcc -Wall -c a.c
a.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
1 | foo() {}
| ^~~
a.c: In function ‘foo’:
a.c:1:8: warning: control reaches end of non-void function [-Wreturn-type]
1 | foo() {}
| ^And I would too... but sometimes there are too many local variables involved to do that cleanly. And, ultimately, if you are making your code less clean to avoid using a goto then you avoided unwisely.
> Can you give an example of where you've used goto in C or a C-like language?
Well there have been several fairly normal examples cited by others. Let me set out my stall and present a more controversial one.
Suppose I have a switch() block (which is sorta a glorified goto already!) and I have two cases that have the same epilogue. For example:
switch (get_next_thing_to_do()) {
case ACTION_A:
do_action_a();
goto common_post_action_cleanup;
case ACTION_B:
do_action_b();
common_post_action_cleanup:
cleanup_something();
z = nullptr;
x = x->next;
// yadda yadda
break;
case ACTION_C:
do_action_c();
// NOTE: we don't need to do the "cleanup" here
break;
}
Now the first thing you should try is to put all of the shared code in its own function. Again, this isn't always practical if what you need to do effects a lot of local variables. It also means that those "cleanup" steps are now implemented far away from the rest of the logic which might hurt comprehensibility on its own.Another option is to just suck it up, repeat the code twice, and then just trust that the compiler will clean it up. First, that won't be as attractive if you have to do it 10 times instead of twice. Second, you're now violating the DRY principal which to me is sacrosanct. Experience has taught me that if someone updates the cleanup code for ACTION_A they will forget to update ACTION_B at the same time. If you care that the two cases always end with the same epilogue you need to have a single copy of the code.
You can try to get back into compliance with DRY by using something like a C macro, but now you're not winning the war against ugly code.
Finally you could just put the cleanup code after the switch(), either by retesting the enum value (requiring it to be stored in a temporary variable) or by adding some other new cleanup_needed boolean flag. But now we're adding control flow that wasn't there before. I personally believe that doesn't help readability or maintainability. You're now just adding more variables to understand and probably getting worse generated assembly to boot.
While many programmers will look at this and feel vaguely icky about the presence of a "goto" if you really stop and look at the block of code it's actually quite readable...at least as long as the label and the goto are near each other. It is immediately apparent that two "states" are sharing some code and why.
This little example is obviously made up. However I've spent a lot of my career writing things like hand-crafted state machines and situations exactly like this (where two "states" are different but share the same epilogue) comes up all the time. State machine code like this is famously hard to read but often sharing code via goto like this is the least-bad option for expressing non-hierarchical control flow.
The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time.
Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks } didn't exist.
Indeed the first thing you do when you try to understand code of that era is to print it out, get your markers out, and start drawing arrows all over the green-bar paper so you can start to make some sense of where control flow is going.
In that world, yes, GOTO was a big problem and its use was rightly replaced with the structured control flow that we all use today. But that world has also been nearly gone for 30 years now. It's completely unrelated to using a C "goto" statement with a well-chosen label name in order to accomplish some otherwise-awkward control flow.
On projects I've worked on I tend to develop a reputation for heavy goto use and I do so unapologetically. Sometimes it honestly is the cleanest way to implement something and I don't think it should be feared one bit.
Just because it was a bad idea to do everything with a GOTO 60 years ago shouldn't mean don't do some things with goto today.
Yes, and that's the rub.
It wouldn't be as bad if there was a way to make the ssh client manage the lifetime of the agent process itself (similar to ProxyCommand or something) And I definitely looked into building something like that.
Ultimately I thought that something built into the client was better overall for a couple reasons: * Filtering rules can be easily managed as .ssh/config settings * As I mentioned in the PR, I was able to reuse a lot of the existing code for the agent protocol that already is compiled into the client.
I also think the "filter" approach makes more sense than having truly separate ssh-agent binaries running. For one thing it's flexible to allow for multiple hops. Imagine if you are ssh'ing from A->B->C. On the first hop I want to give "B" access to keys "K1,K2" but then it wants to only give access to "K2" to C. With a protocol-filtering approach both hosts can prune back the amount of access being forwarded onwards.
Running multiple agents is also a bit ugly, especially if you are trying to consolidate your keys with an agent integrated with your desktop environment, which I think is the most common use case.
FWIW my proposal for fixing it is https://github.com/openssh/openssh-portable/pull/233 but it isn't the most elegant solution either I guess. It doesn't seem to have picked up much interest so I don't think it's likely to ever be merged (at least in its current form) which is fine. Hopefully some tamed version of agent forwarding appears directly in openssh someday, either as a simple key filter or something more complicated like guardian-agent
By far the biggest space user is music. I own thousands of CDs and so have a huge (and entirely legal!) iTunes library of over 50K songs. Back in the days of the 40GB iPod I kept it synced against a complicated smart playlist that tried to include what I was most interested in listening to... with very little success: any time something popped into my head I'd find it wasn't there.
The introduction of the 512GB iPhone models was the first time that I could sync it ALL to my mobile device, no compromises. Now does it make logical sense in the age of Spotify to have 4000 hours of audio synced to my phone? Probably not. However having access to even the most obscure material in my CD collection wherever I go (and even if I don't have internet access) gives me the sensation that I'm living in the future.
More broadly I tend to think of my phone like a biometric-protected SSD that I always have with me. I try to keep a lot of my personal files synced from my desktop to it (mostly using Secure Shellfish right now)
In the vaccination you received, there was a controlled dosage of the active ingredient. This had been tested in thousands of people and is known to give a certain amount of immune response and an acceptable amount of side effects.
When you get have contact with someone who is infectious there is a huge variation in how much viral load they might have and how much they shed. So the first person you mean might only "gift" you a tiny dose that doesn't improve your post-vaccination immunity much at all. The next day you might meet a super contagious person, get basically drenched in viral particles, and then have a breakthrough case that makes you sick for weeks.
It's the sort of "clever hack" that probably sounds appealing to a lot of software engineers but is a terrible public health plan.
In SF in the 1980s, SOMA was one of the cheapest places even in a cheap city like SF... lost of broke creative types living in old warehouse/industrial space that would otherwise be going derelict.
In lower Manhattan whole neighborhoods were in a similar state -- bohemian artists occupying loads of space that nobody else would touch.
And in London plenty of neighborhoods were full of squats. Housing stock had become so worthless that it wasn't even worth the effort to prevent people living there for free.
Now those same neighborhoods are among the most expensive urban areas in the English-speaking world. People are literally spending millions of dollars to live at those exact same street addresses... in some cases even living in the same structures, just instead of a half-derelict warehouse it'll now be all marble countertops and the like.
All of these places have their own stories and unique factors, but it's also very remarkable how similar the trajectories and how extreme the swing has been.
The claim being put forth is that crypto's environmental cost FAR outweighs its utility.
Your counterargument is that such concerns are unraisable simply because one can avoid personally using BTC. Or to put another way "if WE destroy the environment it's none of YOUR business"
And that is wrong -- the atmosphere is shared by all. Changes to its composition are everybody's shared concern.
If you want to argue that some other business's activity are too damaging to the environment to be allowed to continue... fine, make those arguments. But don't bat away concerns about crypto's absurd environmental cost as "none of your darn business"
The original spring 2020 outbreak in the UK was huge, affecting a sizable percentage of the population. However at that time PCR testing was nowhere near scaled up to the point that all of these cases could be tested.
Therefore if you were infected in the first wave, recovered, and are now reinfected 16 months later you probably aren't going to be one of those 137 because it's quite unlikely you were PCR tested the first time around.
That's not to say reinfection is super-common either, but it's probably at least an order of magnitude (or possibly even two) more common than that raw percentage would suggest. In other words, you've produced a statistic with zero significant figures of accuracy.
Anyone who has watched the fourth quarter of an American football game will know how paramount clock management is to that sport. Even more infuriating (for me) is basketball where the losing team is constantly stopping the clock with fouls to prevent the winning team from running down the final seconds... the last "minute" of the game can feel like an eternity.
At least soccer's timekeeping has the benefit of being simple. It works the same way whether a match is being played in the park or in a huge stadium. Personally I prefer it to having a clock that stops and starts like other sports have.
> Added time was introduced because of an incident which happened in 1891 during a match between Stoke and Aston Villa. Trailing 1–0 and with just two minutes remaining, Stoke were awarded a penalty. Villa's goalkeeper kicked the ball out of the ground, and by the time the ball had been recovered, the 90 minutes had elapsed and the game was over
If you didn't let the ref continue the game to their discretion it would make time-wasting tactics even worse. At least now if a team is adjudged to be taking the piss with the timewasting the ref can just mentally tack another minute or two onto the game. It's good that there isn't a "hard stop" that will end the game even if an attack is in full flow.
Suppose you wanted to run a normal electric train up a mountain. It would certainly take a decent amount of power but not so much that it would be a big challenge for a city-scale power grid. So far you're not yet talking the scale of power where storing it would really be interesting to a grid.
One option to store more power would be to make the train much, much heavier. Sounds simple enough -- fill all of the cars with concrete and now hauling it up the mountain will store a lot more power. However now the rails and the trains themselves will need to be far sturdier than a normal railway, and will wear out quickly.
The other option will be to simply scale up -- start the day with a hundred trains in a rail yard at the bottom of the mountain and over the course of a day move them all to a rail yard at the top. Now you have successfully stored a decent amount of juice.
But hold on a minute... you've now built two large rail yards, meaning you'll need a lot of relatively flat real estate at both altitudes. How about instead you just dig a hole on each side, called it a reservoir, and put a pipe between the two? Certainly it must be a lot easier to store and move mass in the form of water than it is in the form of trains!
That is why I don't see much potential in rail-based storage: if you have the geography to build one at-scale, probably you could build pumped hydro there cheaper. Even if you were in a water-scarce area where you would need to enclose both reservoirs to avoid evaporation loss it still sounds simpler to me than building and maintaining a hundred heavy trains would be. Also, routing a pipe between two reservoirs is a lot more flexible than building a railway.
But I also wouldn't be particularly sad if it disappeared.