User not logged in - login - register
Home Calendar Books School Tool Photo Gallery Message Boards Users Statistics Advertise Site Info
go to bottom | |
 Message Boards » » Stupid Software Engineering & Design Decisions Page 1 2 3 4 5 [6] 7, Prev Next  
FroshKiller
All American
51271 Posts
user info
edit post

We have a class for building HTML forms so you don't have to write HTML. It's largely undocumented, which is annoying. I was interested in using it, though, and noticed two public subroutines that seemed to be related to adding instances of our control types to the form. Here are their signatures:

Public Sub AddControl(Label As OurCustomLabelClass, Control As OurBaseControlClass)
Public Sub AddInteriorControl(Control As OurBaseControlClass)


I was interested in adding some checkboxes to the form. Our custom checkbox class renders its own label element. So I did what I thought was the natural, sane thing and opted for the subroutine that didn't have a required parameter of the label type.

The form did not render with any of my checkboxes. When I stepped through the code, I saw that they definitely got instantiated, and the calls to the subroutine definitely ran without exceptions.

I spent about 30 minutes troubleshooting this fucking thing until I finally searched the code base for another instance of someone adding a checkbox to a form through this builder. Care to guess what they did?

They called AddControl and passed Nothing for the Label parameter.

[Edited on April 24, 2017 at 12:53 PM. Reason : Peace, we outta here.]

4/24/2017 12:53:45 PM

aaronburro
Sup, B
51962 Posts
user info
edit post

Yeah, in a case like that, I would opt for AddControl first, based solely on the strange name of AddInteriorControl. Undocumented code is the best.

4/27/2017 12:34:22 AM

FroshKiller
All American
51271 Posts
user info
edit post

I understand why this is, but I'm annoyed as shit that getting the weekday from VB's DatePart function returns a value that doesn't naturally line up with .NET's DayOfWeek enum.

5/9/2017 7:56:43 AM

CaelNCSU
All American
6329 Posts
user info
edit post

Feature: Really important switch in the UI for turning on and off live programming.

1) Create web app for button to toggle live events on/off
2) Web app makes post request to 2nd web app
3) Web app #2 puts message on a Queue
4) Consumer of queue picks up the message and updates the database.
5) Web app #3 displays results in the UI to the end user.

No tests around any of the individual pieces or the system as a whole, so it breaks, constantly and no one knows why.

5/11/2017 7:32:46 PM

JeffreyBSG
All American
10147 Posts
user info
edit post

OLD remote environment was a nice pseudo-desktop in which you could edit your code in GEDIT and save it with CTRL-S

NEW remote environment is a cocksucking folder-navigation system with this dickless web-browser text window, in which you have to click the SAVE button to save anything

really fucking annoying to have to click a button with your mouse every time you want to save

5/11/2017 10:29:47 PM

FroshKiller
All American
51271 Posts
user info
edit post

Someone who should've known better checked some garbage code for generating a spreadsheet that he found on some damn message board into the common application framework, and other people have already started using it, so now we're chained to the goddamn thing.

5/26/2017 1:42:10 PM

FroshKiller
All American
51271 Posts
user info
edit post

A concise illustration of everything wrong with this codebase:

If bSendPhotos = -1 Or bSendPhotos = 1 Or bSendPhotos = 2 Then

6/14/2017 7:53:16 AM

smoothcrim
Universal Magnetic!
18411 Posts
user info
edit post

^^ you sure the stupid decision isn't the code review process?

6/14/2017 9:19:20 AM

FroshKiller
All American
51271 Posts
user info
edit post

That would be the larger problem, yes.

6/14/2017 9:44:09 AM

FroshKiller
All American
51271 Posts
user info
edit post

The spreadsheet code I mentioned before is a "class" that consists solely of shared methods, not a single field nor property nor even private data to be seen.

7/14/2017 7:32:56 AM

FroshKiller
All American
51271 Posts
user info
edit post

I just clicked on a region named "Public Property," and the only thing I saw on screen when it expanded was about 20 lines of private fields.

8/9/2017 8:37:12 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

I hate when people hide fields in regions like that. Group sections of related code, if you must, but fuck off with regions just to say where private fields are.

Anyway, the product I work on has a validation system that we built that is, to say the least, buggy. It's solid most of the time, but it does have some very nasty potential for race conditions. The end result is a way to validate some small piece of the VM, and provide an validation status (pass/fail/warning) and error message. The application ends up displaying all of the errors and warnings in a status bar. The validation pieces are VMs themselves. It's rough, but it's hobbling along nicely.

Well, our genius offshore guy, who I wouldn't hire as a junior dev, gets assigned a task to require user acknowledgement of something before you can submit from the CRUD screen. We already have a pattern for this in our validation structure, and it does a standard pop-up. Or, you could have a button that shows up dynamically on the CRUD screen when applicable. Whatever. Pretty standard stuff, right?

Well, this guy decides "fuck all that. I'm gonna go add a button to the validation VMs, which are asynchronously created and recreated as needed. And I'm gonna make sure this button shows up on EVERY validation result, not just the one I've created." So, now, our error messages and warnings ALL have a button on them which does nothing, except for his new one.

When I called him on it, his response was "I tested it, and it works fine."
I reverted it.

BTW, this is the same guy who needed to add a button to our settings screen, which is dynamically generated. It loads a list of settings and reads them into a generic "ConfigSetting" type, where the generic parameter is some kind of serializable construct, and the whole things is autoserialized into a database somewhere. These ConfigSettings get read at app startup, and when you load the settings screen, it just creates a list of settings that can be edited. OK, so the system isn't set up very well to allow just throwing a button on there to perform a certain action... So what does this guy do? Rework the screen to allow a custom view to be added? Of course not. He creates a ConfigSetting{Button} that gets auto generated, providing you the button, which you then can click.

It kinda works, except for a small problem. A button isn't really deserializable. So, as long as you never save your settings, you are fine. But the moment you save, that button gets "persisted" into the database, and then the app won't load anymore, because HOW THE FUCK DO YOU DESERIALIZE A FUCKING BUTTON. And the only fix is to delete ALL of your settings from the database, because we store all settings in a single, binary object, that can't easily be pulled out.

[Edited on September 2, 2017 at 4:45 PM. Reason : ]

9/2/2017 4:33:27 PM

FroshKiller
All American
51271 Posts
user info
edit post

That dude sucks, but can't you strip the buttons from the settings before saving?

I just broke a release build, because the release build system uses an older version of the compiler than, like, every other programmer and every build configuration in the QA environment.

9/19/2017 8:42:01 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

Well, sure, but we shouldn't have to write code to strip out buttons from settings... Ultimately, I spent about half a day doing what the original dev who wrote our settings code should have done: give us a way to specify a different UI for certain settings categories.

9/24/2017 3:05:19 PM

aaronburro
Sup, B
51962 Posts
user info
edit post

I would recommend that you not use the US Post Office's online customs forms submission page right now. It seems to work pretty well, except for a small problem... When you're done, you can click a link to see your completed form. That link is the same for everyone. And what you will see is the last form that was submitted by anyone, whether it's your form or not.

It also appears that they are testing their code in production, as you can see a lot of forms coming through for John Doe and the like...

Yeah... I'd stay away from that part of their website until they get it fixed.

1/25/2018 7:36:54 PM

smoothcrim
Universal Magnetic!
18411 Posts
user info
edit post

^

I'm working on a compliance lib for nist 800-53. For those in the know, that's 1200ish controls (rules) about your infrastructure. This lib needs to be written in the language we wrote (functional DSL on top of haskell). I say, you know, rather than writing these all by hand, we should auto generate them. Our constructors are the only values we need to constrain and there's a well defined grammar for that.

No no no, that'll take too long to get started, we need something now! Just brute force it!
Despite my plea, here we are, several sprints later, barely making a dent, writing really menial code.

So a coworker and say fuck it and go write the tool and he helps me put a js front end on it. Now anyone can create the rules, just like an outlook inbox rule. Took us a week to write the tool in spare time.

1/26/2018 8:20:40 AM

FroshKiller
All American
51271 Posts
user info
edit post

Wasted a good few hours this morning because whatever shitbird wrote the configuration front end for an internal service swallowed exceptions on saving changes. The user didn't have permission to write to the config file, but you'd never know it from the front end.

2/12/2018 10:40:09 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

if (foo is SomeType)
{
if (((SomeType)foo).Property1 == "bar" && ((SomeType)foo).Property2 > 3 && ((SomeType)foo).Property3 is FooBar && ((FooBar)((SomeType)foo).Property3).WhatTheFuck))
{
((SomeType)foo).RunMethod();
((FooBar)((SomeType)foo).Property3).DoSomeShit();
((FooBar)((SomeType)foo).Property3).ShitWasDone = true;
}
}


Saw this shit in the codebase last week. Told the guy to cut that shit out and just use an as-cast so that he only casts once. His response? "I don't want to do the extra allocation, and direct casts are faster than as-casts." Bruuuuuuuuuuuuuuuuuuuuuuh.

Oh, and an hour-and-a-half later, he checks in more of that shit

[Edited on March 4, 2018 at 10:12 PM. Reason : ]

3/4/2018 10:04:55 PM

FroshKiller
All American
51271 Posts
user info
edit post

I like how it's also a leaky abstraction.

I eliminated all dependencies on an internal framework from a service after being specifically told not to waste my time on it, and the build system is still configured to build the framework first to satisfy the dependencies. I guess I'm the asshole.

[Edited on March 6, 2018 at 10:29 AM. Reason : ///]

3/6/2018 10:28:01 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

Two months later, and the Post Office STILL hasn't fixed their website. I'm about to blast this ish on Facebook.

3/21/2018 9:33:54 PM

aaronburro
Sup, B
51962 Posts
user info
edit post

Dude at work failed the FizzBuzz test this week. He no longer works for us.

5/4/2018 10:33:13 PM

CaelNCSU
All American
6329 Posts
user info
edit post

How do you fail fizzbuzz if you already got a job?!

5/5/2018 9:33:14 AM

jaZon
All American
26976 Posts
user info
edit post

Quote :
"Two months later, and the Post Office STILL hasn't fixed their website. I'm about to blast this ish on Facebook."


No idea if it's still doing it, but I was considering getting a PO Box about a month ago and the fucking thing kept redirecting to a Brooklyn location

5/5/2018 3:24:17 PM

aaronburro
Sup, B
51962 Posts
user info
edit post

^^ Hell if I know. That's his problem now

5/6/2018 12:05:17 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

Saw IValueConverters yesterday which have side effects. Good times

5/9/2018 7:58:41 AM

CaelNCSU
All American
6329 Posts
user info
edit post

I took over an API that had toString methods with loads of side effects. Was the point I decided it was hopeless to fix it and throw it away. Sometimes a rewrite is better.

5/9/2018 9:08:39 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

Side-effects man has struck again... Created a method named "FooHasErrors" which takes a Foo and tells you if it has errors. It also changes the value of multiple instance fields on the class the method is defined on. Like, wtf.

5/25/2018 11:04:15 PM

FroshKiller
All American
51271 Posts
user info
edit post

I can't get into specifics, but I saw a massive cock-up with a password reset feature.

The user requests a password reset. Fine. The application sends them an email with a reset link. The reset link includes a unique token as a query string parameter.

The password reset screen validates the unique token. If the token is assigned to a user, then the visitor gets a form with two visible fields: one for the new password and another to confirm the new password.

Two visible fields.

A few hidden fields.

One of them is the unique token.

The application sets the new password for the user with the unique reset token submitted with the form.

Hey, the application validated the token in the query string parameter. No need to validate the token submitted on the form, right?

What do you think might happen if a user followed a valid reset link but stripped the token out of the form when they submitted?

6/12/2018 9:10:40 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

The server component for the system I work on has a data type that is binary in nature. It's either one value or it's the other. Forward = true, backward = false, kind of a thing, right? It's a common data item that, even across usage domains, exists in all of them. Different usage domains may have different forms, but it's still a binary idea: it's either one thing or it's the other. So, imagine it's A or B in one domain, C or D in another, E or F in yet another. A, C, and E represent the same idea, while B, D, and F represent the other side.

This isn't a particularly complex thing, but for some odd reason, the server team basically set up the entire codebase so that it can only handle, say, A or B. Even though we know C, D, E, and F exist, and they exist in the system that this server code is replacing.

Well, we are now getting into the domain use cases for the C and D values. Users don't want to see A and B in the UI for these domain use cases, which incidentally are in the same data sets as the A/B domain. So, the server guys, brilliant as they are, want us to do Coherence cache lookups for each row on real-time updating screens with tens of thousands of records to figure out which domain the record belongs to, and then convert A to C/E/etc and B to D/F/etc based on the record's domain. Because apparently it's going to be THAT difficult for them to fix the server code to allow the same domain values that the system we are replacing already handles... I have a sinking suspicion that the values for A and B are hard-coded as strings throughout the entire server codebase... because reasons.

7/26/2018 11:20:47 PM

smoothcrim
Universal Magnetic!
18411 Posts
user info
edit post

why wouldn't the value be stored as a bool and then the client side could do the value swap in the presentation layer? Why would coherent cache looks be necessary for this?

7/29/2018 9:40:22 AM

aaronburro
Sup, B
51962 Posts
user info
edit post

Storing it as a bool would be fine. Hell, internally modelling this as a bool is fine, too, just keep it internal; it's better than strings, for fuck's sake. As the client, I don't give a rat's ass how it is stored or modeled internally. I do care how you expose that data to me, and how much work I have to do to get it into the format I need as the client. You could do the value swap in the presentation layer, but we have multiple client applications (different user types and views). I don't like having multiple client applications having to implement identical conversion logic, especially if they could just submit what they want the display value to be and have it returned back to them. I'd even be fine with the "standard" value and the domain-specific value.

The coherence lookups are necessary because the information needed to perform the value swap is not readily available in the data rows. You have to do a bit of a deep dive to figure out the differences, and it doesn't make sense to expose all of that information in the data rows (the user will never want to see that info), especially if it could just be done as a SQL transformation on the back-end which then makes it available to literally every consumer of the data.

8/5/2018 4:01:00 PM

smoothcrim
Universal Magnetic!
18411 Posts
user info
edit post

I try to push the logic client side to keep my TPS up, but I'm guessing this isn't a super high performance app if you're using SQL

8/5/2018 9:57:21 PM

FroshKiller
All American
51271 Posts
user info
edit post

Shaggy alert

8/6/2018 8:05:30 AM

FroshKiller
All American
51271 Posts
user info
edit post

I just found two different functions for returning an ordinal indicator for an integer in the same class on the same screen.

The first is general, and the second is specific to ordinals for a day. The second need not even exist. Amusingly, the first is literally copied & pasted from an ancient vbforums.com thread, comments and all.

I'll give you the first one straight up:

    Private Function AddOrdinalSuffix(ByVal num As Integer) As String

'can handle negative numbers (-1st, -12th, -21st)

Dim last2Digits As Integer = Math.Abs(num Mod 100)
Dim lastDigit As Integer = last2Digits Mod 10

'the only nonconforming set is numbers ending in <...eleventh, ...twelfth, ...thirteenth>
'all other numbers are as 1 thru 10 */
If ((last2Digits >= 11 AndAlso last2Digits <= 13) OrElse lastDigit > 3) Then lastDigit = 0

'lastdigit is now a number in range < 0, 1, 2, 3>
Return num.ToString() + New String() {"th", "st", "nd", "rd"}(lastDigit)

End Function


How do you like that illegible fucking return statement?

This inspired me to go hunting for other duplicated code doing the same thing in this exact same solution. I found several instances!

One is just a complete lift of the date-specific one in a different project. Unfortunately, that date-specific one is in the common library code, soooooooo no need whatsoever to have lifted that.

Another, which might be my favorite, returns a string with the number and its ordinal in a <sup /> tag. It's so great, because it even has a bug (11 => "11<sup>st</sup>", 12 => "12<sup>12nd</sup", 13 => "13<sup>rd</sup>", and so on).

But the stupidest fucking implementation has to be these little inline beauties I found used twice in the same class:
Dim ordinals() As String = {"", "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"}


And as you probably noticed, we have a hard-coded assumption of English despite having support for multiple languages.

[Edited on August 20, 2018 at 8:39 AM. Reason : ///]

8/20/2018 8:39:23 AM

moron
All American
31781 Posts
user info
edit post

That’s not a bad return statement compared to a LOT of things I’ve seen

If I’m not feeling lazy later I’ll post probably the craziest code you could write to determine if a date falls within a time period

8/20/2018 11:29:35 AM

qntmfred
retired
39348 Posts
user info
edit post

^ i was pondering that exact problem last night after using Yelp's "Open at XX:XX Time" feature. I was mostly curious what data structure is best suited for storing the operating hours of a business, and how to accommodate seasonal or holiday hours, while also ensuring reasonably performant search, at scale.

8/20/2018 12:46:19 PM

afripino
All American
10404 Posts
user info
edit post

google and facebook both have me sending pipe-delimited files daily to update hours of operation for some restaurants.

8/20/2018 1:26:57 PM

moron
All American
31781 Posts
user info
edit post

^^ what would be the problem with just storing them as "unix time" offsets or similar?

At that point, it's just a simple "greater than" and "less than" operation

8/20/2018 1:45:15 PM

FroshKiller
All American
51271 Posts
user info
edit post

Way to commit yourself to a time series for no reason, dude.

8/20/2018 2:03:46 PM

CaelNCSU
All American
6329 Posts
user info
edit post

Data structure is typically a segment tree or interval tree. It shows up in interviews from time to time.

https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/

https://en.m.wikipedia.org/wiki/Interval_tree

8/21/2018 12:56:01 PM

moron
All American
31781 Posts
user info
edit post

Here's the code a former colleague wrote to determine if a date comes in the past... this guy actually had 10 years of experience and usually did okay work, not sure where this came from:

                      DateTime dateTime1 = expired.deleteOnDate;
int dayOfYear1 = dateTime1.DayOfYear;
dateTime1 = DateTime.UtcNow;
int dayOfYear2 = dateTime1.DayOfYear;
int num;
if ( dayOfYear1 < dayOfYear2 )
{
DateTime dateTime2 = expired.deleteOnDate;
int year1 = dateTime2.Year;
dateTime2 = DateTime.UtcNow;
int year2 = dateTime2.Year;
num = year1 > year2 ? 1 : 0;
}
else
num = 1;
if ( num == 0 )
{...}

8/29/2018 2:01:40 PM

aaronburro
Sup, B
51962 Posts
user info
edit post

I can think of no better way to do that. None. Not a single one.

8/31/2018 8:22:41 AM

smoothcrim
Universal Magnetic!
18411 Posts
user info
edit post

he even converts to utc in there..

8/31/2018 2:27:24 PM

FroshKiller
All American
51271 Posts
user info
edit post

Nah, he doesn't. He compares it to the current date & time in UTC. No reason to think expired.deleteOnDate isn't in UTC to begin with. It looks like C#. In .NET, DateTime doesn't include any time zone information, so it's common to either use it as local system time or UTC.

8/31/2018 5:50:45 PM

FroshKiller
All American
51271 Posts
user info
edit post

I've been thinking about that dumb code.

I assume expired.deleteOnDate is a regular-ass DateTime structure with a Kind of Unspecified, and I assume it's being used as UTC even though that's not made explicit. Let's walk through this motherfucker.

                      DateTime dateTime1 = expired.deleteOnDate;
int dayOfYear1 = dateTime1.DayOfYear;
dateTime1 = DateTime.UtcNow;
int dayOfYear2 = dateTime1.DayOfYear;

Okay, so the declaration, assignment, and reassignment of dateTime1 are all dumb and unnecessary, and the variable name is bad.
                      int num;
if ( dayOfYear1 < dayOfYear2 )
{
DateTime dateTime2 = expired.deleteOnDate;
int year1 = dateTime2.Year;
dateTime2 = DateTime.UtcNow;
int year2 = dateTime2.Year;
num = year1 > year2 ? 1 : 0;
}
else
num = 1;

Another unnecessary local with a bad name. So if the day of the year of deleteOnDate is less than the current day of the year (UTC), we compare the years. If the year of deleteOnDate is greater than the current year, it sets num to 1. Otherwise, it sets num to 0.

If the day of the year of deleteOnDate is greater than or equal to the current day of the year, though, we set num to 1 straight away.

So what the hell is num? I guess 0 means "the deleteOnDate is earlier than today's date," and 1 is supposed to mean the opposite. But there's a big bug here! If deleteOnDate is a later day of the year from a prior year, we're gonna get a 1 back instead of a 0.

This feels like the dude was trying to be clever by comparing the day of year first, but it's especially dumb because the goddamn comparison operators for DateTime actually compare ticks, which is a straight long-to-long comparison.

The only other thing I can think of is that maybe the year doesn't matter, but in that case, why bother using a fucking DateTime as the data type for the deleteOnDate field...?

9/5/2018 10:58:34 AM

scud
All American
10788 Posts
user info
edit post

I found this today and my brain melted - paraphrasing a bit:

Starts off ok - not terrible not great



Cache m_Cache;

public void addDatum( Item item ) {
addData( new Item[] { item } );
}

public void addData( Item[] items ) {
ArrayList a1 = new ArrayList( items.length );
for( int i=0; i<data.length; i++ )
if( filter( items[i] ) )
a1.add( items[i] );

sort();

... another index loop
...some event handing
}


Ok here's a consumer of the above....uhm why?

public void setCache( Collection data ) {
Iterator iter = data.iterator();
while( iter.hasNext() )
addDatum( iter.next() )
}


If that weren't bad enough - I had the unique joy this morning to discover the sort method:

public synchronized void sort() {
m_collection = Collections.synchronizedSet( new TreeSet(m_collection) ) {
public boolean add( Object o ) {
...//null handling
super.remove( o );
super.add( o );
};
});
}

9/6/2018 9:14:56 PM

FroshKiller
All American
51271 Posts
user info
edit post

Enjoy this lightly anonymized shit sandwich:

Dim recordTimeZone = OmniUtil.ParseInt32(New DataLayer.Repository.ConfigurationTableRepository(FrameworkDataLayer.Data.DatabaseType.AppPrimary).GetByRecordId(myRecordID).RecordTimeZone)


No explicit type declared for recordTimeZone. It gets set to an integer! Hint: It's supposed to be an enum.

OmniUtil.ParseInt32 does its goddamnedest to get you an integer out of whatever object you throw at it, defaulting to 0 in the case of a null reference. But do please take a look at that fucking shitpig of an expression being passed in as its only parameter:
New DataLayer.Repository.ConfigurationTableRepository(FrameworkDataLayer.Data.DatabaseType.AppPrimary).GetByRecordId(myRecordID).RecordTimeZone


ConfigurationTableRepository is a class for retrieving a specific type of record from a specific table in our application's databases for a given database type. No exception handling around the constructor call there.

We chain directly into calling GetByRecordId, again with no exception handling. GetByRecordId can return a null reference, which means that chain into its RecordTimeZone property getter is just as likely to throw a null reference exception as anything.

The cherry on top: RecordTimeZone is a nullable 16-bit integer, because some knucklehead generated the model class with a tool that did an opinionated examination of our dogshit schema then never bothered to clean it up.

9/7/2018 2:03:31 PM

FroshKiller
All American
51271 Posts
user info
edit post

Injected a bit of T-Dub into the code base with setup & cleanup subs in a test project named SetEmUp and OhWeBack.

Though come to think of it, should OhWeBack be PeaceWeOuttaHere?

[Edited on October 9, 2018 at 1:49 PM. Reason : ///]

10/9/2018 1:48:49 PM

CaelNCSU
All American
6329 Posts
user info
edit post

@scud naming fail: no disambiguation without differentiation.

Do things access m_collection outside the sort method?

10/9/2018 11:44:28 PM

Lionheart
Costar un huevo
12593 Posts
user info
edit post

Anyone have any good recommendations on books or articles related to project planning and resource management? Shifting more towards a planning role in the new year and hoping to brush up a bit on the latest trends and thoughts.

12/6/2018 10:33:33 AM

 Message Boards » Tech Talk » Stupid Software Engineering & Design Decisions Page 1 2 3 4 5 [6] 7, Prev Next  
go to top | |
Admin Options : move topic | lock topic

© 2019 by The Wolf Web - All Rights Reserved.
The material located at this site is not endorsed, sponsored or provided by or on behalf of North Carolina State University.
Powered by CrazyWeb v2.37 - our disclaimer.