Negative numbers

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Negative numbers

Post by zhiayang »

Help! I've recently implemented RTC in my OS. As you may know, however, not everyone lives in central time (duh), so I've implemented a way to set the timezone. Positives values work fine, however when I try to set a negative timezone, **** happens.

Code: Select all


		retnum *= -1;
		kprintf("%d", (signed long)-62);

I have this implemented this in my atoi function, where if my first character is a '-', it would execute this. The number itself works perfectly fine, the timezone gets offset by negative amounts. However, the kprintf function screws up. As you can see, I changed the variable into a constant and even cast it into a (signed), it doesn't work. kprintf will display random characters.

Here is the kprintf code:

Code: Select all

void kprintf(const char *str, ...)
{
	va_list listptr;
	va_start(listptr, str);
	
	short is_percent = 0;
	short colour_changed = 0;
	
	register int i = 0;
	for(; i < strlen(str); i++)
	{
		if(str[i] == '%')
		{
			is_percent = 1;
			continue;
		}	
		if(is_percent)
		{
			switch(str[i])
			{
				case '%':
					//Gotta print % itself
					putch('%');
					break;
				
				case 'i':
					putnum(va_arg(listptr, int));
					break;
					
				case 'd':
					putnum(va_arg(listptr, long));
					break;
					
				case 'c':
					putch((char)va_arg(listptr, int));
					break;					
							
				case 'x':
					puthex(va_arg(listptr, unsigned int));
					break;		
					
				case 's':
					puts(va_arg(listptr, char*));
					break;
					
				case 'u':
					putunum(va_arg(listptr, unsigned long));
					break;
					
				case 'f':
					putfloat(va_arg(listptr, double));
					break;
					
				case '~':
					
					//Start messy if-else...
					
					if(str[i + 1] == 'B' && str[i + 2] == 'L' && str[i + 3] == 'A')
					{
						set_text_colour(BLACK);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'B' && str[i + 2] == 'L' && str[i + 3] == 'U')
					{
						set_text_colour(BLUE);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'G' && str[i + 2] == 'R' && str[i + 3] == 'E')
					{
						set_text_colour(BLUE);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'C' && str[i + 2] == 'Y' && str[i + 3] == 'A')
					{
						set_text_colour(CYAN);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'R' && str[i + 2] == 'E' && str[i + 3] == 'D')
					{
						set_text_colour(RED);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'M' && str[i + 2] == 'A' && str[i + 3] == 'G')
					{
						set_text_colour(MAGENTA);
						colour_changed = 1;
					}					
					else if(str[i + 1] == 'B' && str[i + 2] == 'R' && str[i + 3] == 'O')
					{
						set_text_colour(BROWN);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'G' && str[i + 3] == 'Y')
					{
						set_text_colour(LIGHTGREY);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'D' && str[i + 2] == 'G' && str[i + 3] == 'Y')
					{
						set_text_colour(DARKGREY);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'B' && str[i + 3] == 'U')
					{
						set_text_colour(LIGHTBLUE);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'G' && str[i + 3] == 'E')
					{
						set_text_colour(LIGHTGREEN);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'C' && str[i + 3] == 'Y')
					{
						set_text_colour(LIGHTCYAN);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'R' && str[i + 3] == 'E')
					{
						set_text_colour(LIGHTRED);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'M' && str[i + 3] == 'A')
					{
						set_text_colour(LIGHTMAGENTA);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'L' && str[i + 2] == 'B' && str[i + 3] == 'R')
					{
						set_text_colour(LIGHTBROWN);
						colour_changed = 1;
					}
					else if(str[i + 1] == 'W' && str[i + 2] == 'H' && str[i + 3] == 'I')
					{
						set_text_colour(WHITE);
						colour_changed = 1;
					}
					else
					{
						putchar(str[i]);
						break;
					}					
					i += 3;
					break;
					
					
				//Precision format handling
				default:
					if(is_number(str[i]) || str[i] == '.')
					{
						int tmp = i;
						while(is_number(str[tmp]) || str[tmp] == '.')
						{
							tmp++;
						}
						char tm[3] = { str[i], str[i + 1] };
						
						switch(str[tmp])
						{
							case 'i':								
							case 'd':
								putnum_precision(va_arg(listptr, int), atoi(tm));
								return;						
							
							case 'u':	
								putunum_precision(va_arg(listptr, unsigned long), atoi(tm));
								return;	
									
							case 'x':
								puthex_precision(va_arg(listptr, unsigned int), atoi(tm));
								return;
												
						}
						
						short precision = 0;
						tmp = i;
						while(str[tmp] != '.')
						{
							tmp++;
							if(is_letter(str[tmp]))
							{
								precision = 10;
								break;
							}									   
						}
						tmp++;
						char t[3] = "";
						
						if(!is_letter(str[tmp]))
						{
							t[0] = str[tmp];
						}
						else if(is_letter(str[tmp]))
						{
							precision = 10;
							goto print;
						}
						
						if(!is_letter(str[tmp + 1]))
						{
							t[1] = str[tmp + 1];
						}
						precision = atoi(t);
						
						print:
						if(is_letter(str[tmp + 1]))
						{
							tmp--;
						}
						switch(str[tmp + 2])
						{
							case 'f':
								putfloat_precision(va_arg(listptr, double), precision);
								break;
						}
						i = tmp + 3;
					}
					break;
			}
			is_percent = 0;
		}		
		else
		{
			putch(str[i]);
		}
	}
	if(colour_changed)
	{
		set_text_colour(WHITE);
	}
	va_end(listptr);
}

What's wrong?

Thanks.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Re: Negative numbers

Post by JamesM »

The important code to see would be the code for putnum.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Negative numbers

Post by gerryg400 »

Code: Select all

            case 'd':
               putnum(va_arg(listptr, long));
               break;
'd' means int not long.
If a trainstation is where trains stop, what is a workstation ?
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Negative numbers

Post by zhiayang »

JamesM wrote:The important code to see would be the code for putnum.
silly me, i overlooked that part. Fixed it now, thanks for jerking my brain. :oops:
User avatar
brain
Member
Member
Posts: 234
Joined: Thu Nov 05, 2009 5:04 pm
Location: UK
Contact:

Re: Negative numbers

Post by brain »

You do know this code may do all manner of messed up things if you end a string with ~ as your colour one assumes that ~ is always followed by 3 letters of colour code, if they are not there it reads random value from the stack... Right now this is ok but there may be problems with this later especially if you let userland code use this function. Just letting you know!

Edit: a precision string on the end of the format without a dot will also cause the pointer to leave the bounds of the string potentially looping forever, from what I can see?
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: Negative numbers

Post by bluemoon »

kprintf() should not be exported to user anyway, they should use printf(), which do the formatting in userland and call kernel with write(...).
User avatar
brain
Member
Member
Posts: 234
Joined: Thu Nov 05, 2009 5:04 pm
Location: UK
Contact:

Re: Negative numbers

Post by brain »

True, but even in userland this function is exploitable and dangerous...
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: Negative numbers

Post by bluemoon »

I agree. So I recommend things like newlib or solar's pdclib, which has certain degree of stability, conform to standards, a rich set of features.
Rolling your own printf() proven to be a boring job and provide no excitement, and may not even met the standard.
Most people only need a minimum subset as kprintf() for debugging purpose.
sandras
Member
Member
Posts: 146
Joined: Thu Nov 03, 2011 9:30 am

Re: Negative numbers

Post by sandras »

Just putting out a somewhat related thought. After trying to implement my equivalent to a variadic printf(), and reading one of Brendan's posts somewhere on the forum, I tried going back to separate prnt_char(), prnt_str(), prnt_uint(), and prnt_sint(), and because of the separation:
*) I saved about 200 bytes;
*) didn't have to use a variadic funtion, implementation of which, I think, is not portable across compilers;
*) complexity of individual functions decreased.

Also, note that I only use print_uint(my_uint)/prnt_sint(my_sint), which internally use prnt_char() for printing, instead of doing prnt_str(atoi(my_int)). This also simplifies things. After all, I do not have the need to use strings converted from integers for anything else than printing.

You have to tell your kernel what to print one way or the other, but why do it in some roundabout way, having a (relatively complex) function, that decodes a string and finds out what you want to print, instead of telling it what to print directly.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Negative numbers

Post by Solar »

Sandras wrote:*) didn't have to use a variadic funtion, implementation of which, I think, is not portable across compilers;
It's perfectly possible.
You have to tell your kernel what to print one way or the other, but why do it in some roundabout way, having a (relatively complex) function, that decodes a string and finds out what you want to print, instead of telling it what to print directly.
You do it "in some roundabout way" because of the principle of least surprise. Every C programmer knows how to use printf(). A function named "kprintf()" sends a clear message on how it should be used. But what your prnt_*() functions do is to place the burden of formatting the output on the user.
Every good solution is obvious once you've found it.
Post Reply