printf + html = ?

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
felipe
Member
Member
Posts: 27
Joined: Tue Sep 28, 2010 8:19 am

printf + html = ?

Post by felipe »

Hi guys, I am following JamesM's tutorial and I decided to write a printf like routine that also accept colors:

Code: Select all

int main () {
  clear_screen();
  print(
"name="
  "<color fg=red>"
    "<str>"
   "<:color>"
", count="
   "<color fg=blue>"
     "<dec>"
    "<:color>" 
", pointer="
  "<color fg=cyan,bg=red>"
    "<hex>"
  "<:color><nl>", "felipe", 1234, 0xffffffbc);
  
  return 0;
}
the above code does what is expected,
what do you think? (If someone is interested in the full code I will post it)
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: printf + html = ?

Post by mariuszp »

Pretty good idea. So could you post the full code?
felipe
Member
Member
Posts: 27
Joined: Tue Sep 28, 2010 8:19 am

Re: printf + html = ?

Post by felipe »

Here it is, mind you I'm still working on it so it probably has a few bugs. Also I have some ideas I want to implement like adding field width and pad so that you can use this

Code: Select all

print("<dec width=8,pad=0>", 21) => 00000021 

Code: Select all

typedef char *va_list;
#define _INTSIZEOF(n)    ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define va_start(ap, v)  (ap = (va_list) &v + _INTSIZEOF(v))
#define va_arg(ap, t)    (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define va_end(ap)       (ap = (va_list) 0)

typedef unsigned int    dword;
typedef unsigned short  word;
typedef unsigned char   byte;

#define NULL ((void *) 0)
#define true 1
#define false 0

void memcpy(void *dest, const void *src, int n) {
  //asm rep movbs ? ##########################################
  int i;
  for (i = 0; i < n; i++)
    ((byte *)dest)[i] = ((byte *)src)[i];
}

void *memset(void *s, int c, int n) {
  //asm rep movbs ? ##########################################
  int *ip = (int *) s;
  int i;
  for (i = 0; i < n; i++)
    *ip++ = c;
}

void strcpy (char *dest, const char *src) {
  while (*src)
    *dest++ = *src++;
  *dest = 0;
}

int strcmp (const char *a, const char *b) {
  while (*a && *b && *a == *b) {
    a++;
    b++;
  }

  if (*a == 0 && *b == 0)
    return 0;
  else if (*a < *b)
    return -1;
  else
    return 1;
}

int strlen (const char *str) {
  int size = 0;
  while (*str++)
    size++;
  return size;
}

void outb (word port, byte value) {
   asm volatile ("outb %1, %0" : : "dN" (port), "a" (value));
}

char *video_mem = (char *)0xb8000;
word cursor = 0;

void scroll () {
  memcpy(video_mem, video_mem + 160, 4000 - 160);
  memset(video_mem + 4000 - 160, 0x07200720, 40);
  cursor -= 160;
}

#define BLACK          0x0
#define BLUE           0x1
#define GREEN          0x2
#define CYAN           0x3
#define RED            0x4
#define MAGENTA        0x5
#define BROWN          0x6
#define LIGHT_GREY     0x7
#define DARK_GREY      0x8
#define LIGHT_BLUE     0x9
#define LIGHT_GREEN    0xA
#define LIGHT_CYAN     0xB
#define LIGHT_RED      0xC
#define LIGHT_MAGNETA  0xD
#define LIGHT_BROWN    0xE
#define WHITE          0xF

byte foreground = LIGHT_GREY;
byte background = BLACK;

void print_char (char c) {
  if (cursor >= 4000)
    scroll();

  if (c == '\n') {
    cursor += 160;
    cursor = cursor - (cursor % 160);
  }
  else {
    video_mem[cursor++] = c;
    video_mem[cursor++] = foreground | (background << 4);
  }

  outb(0x3d4, 14);
  outb(0x3d5, cursor >> 9);
  outb(0x3d4, 15);
  outb(0x3d5, cursor >> 1);
}

void print_string (char *str) {
  char *sp;
  for (sp = str; *sp != 0; sp++)
    print_char(*sp);
}

void clear_screen () {
  int i;
  for (i = 0; i < 2000; i++)
    print_char(' ');
  cursor = 0;
}

void get_tag_name (char *str, char *buffer) {
  str++;
  for (; *str != ' ' && *str != '>'; str++, buffer++)
    *buffer = *str;

  *buffer = 0;
}

char *get_attrib (char *str, char *name, char *value) {
  if (*str == '>')
    return NULL;
  
  while (*str != '=')
    *name++ = *str++;
  *name = 0;

  str++;

  while (*str != ',' && *str != '>')
    *value++ = *str++;
  *value = 0;

  if (*str == ',')
    return str + 1;
  else
    return str;
}

void get_attrib_value (char *name, char *str,
		       char *buffer, char *default_val) {
  while (*str++ != ' ')
    ;

  char name_buffer[32];
  char value_buffer[32];

  while (str = get_attrib(str, name_buffer, value_buffer)) {
    if (!strcmp(name_buffer, name)) {
      strcpy(buffer, value_buffer);
      return;
    }
  }

  strcpy(buffer, default_val);
}

void reverse_string (char *str) {
  int start = 0;
  int end = strlen(str) - 1;

  while (start < end) {
    char temp = str[end];
    str[end] = str[start];
    str[start] = temp;
    start++;
    end--;
  }
}

void int_to_string (dword i, int base, char *buffer) {
  int index = 0;
  
  while (i != 0) {
    int offset = i % base;
    if (offset < 10)
      buffer[index++] = offset + '0';
    else
      buffer[index++] = offset - 10 + 'A';
    
    i /= base;
  }
  buffer[index] = 0;
  reverse_string(buffer);
}

void print_decimal (int num) {
  int negative = 0;
  if (num < 0) {
    negative = 1;
    num = -num;
  }
  
  char buffer[64];
  int_to_string(num, 10, buffer);
  
  if (negative)
    print_char('-');
  
  print_string(buffer);
}

void print_hexadecimal (int num) {
  char buffer[64];
  int_to_string(num, 16, buffer);

  print_string("0x");
  print_string(buffer);
}

byte get_color (char *name) {
  static char *colors[16] =
    { "black", "blue", "green", "cyan",
      "red", "magenta", "brown", "light-grey",
      "dark-grey", "light-blue", "light-green",
      "light-cyan", "light-red", "light-magneta",
      "light-brown", "white" };

  int i;
  for (i = 0; i < 16; i++) {
    if (!strcmp(name, colors[i]))
      return i;
  }
  return -1;
}

void print (char *str, ...) {
  va_list ap;
  va_start(ap, str);

  byte saved_fg;
  byte saved_bg;
  
  char *ptr;
  char tag_name[32];
  for (ptr = str; *ptr; ptr++) {
    if (*ptr == ':') {
      print_char(*(ptr + 1));
      ptr+= 1;
    }
    else if (*ptr == '<') {

      get_tag_name(ptr, tag_name);

      if (!strcmp(tag_name, "color")) {
	saved_fg = foreground;
	saved_bg = background;
	char attrib_value[32];
	
	get_attrib_value("fg", ptr, attrib_value, "light-grey");
	foreground = get_color(attrib_value);
	
	get_attrib_value("bg", ptr, attrib_value, "black");
	background = get_color(attrib_value);
      }
      else if (!strcmp(tag_name, ":color")) {
	foreground = saved_fg;
	background = saved_bg;
      }
      else if (!strcmp(tag_name, "dec")) {
	print_decimal(va_arg(ap, int));
      }
      else if (!strcmp(tag_name, "hex")) {
	print_hexadecimal(va_arg(ap, int));
      }
      else if (!strcmp(tag_name, "str")) {
	print_string(va_arg(ap, char *));
      }
      else if (!strcmp(tag_name, "newline") ||
	       !strcmp(tag_name, "nl")) {
	print_char('\n');
      }

      while (*ptr != '>')
	ptr++;
    }
    else
      print_char(*ptr);
  }
  va_end(ap);
}

int main () {
  clear_screen();
  print(
"name="
  "<color fg=red>"
    "<str>"
   "<:color>"
", count="
   "<color fg=blue>"
     "<dec>"
    "<:color>" 
", pointer="
  "<color fg=cyan,bg=red>"
    "<hex>"
  "<:color><nl>", "felipe", 1234, 0xffffffbc);
  
  return 0;
}
felipe
Member
Member
Posts: 27
Joined: Tue Sep 28, 2010 8:19 am

Re: printf + html = ?

Post by felipe »

berkus wrote:Waste of cpu?
The implementation or the idea? If the former how can I improve the code to run faster? (I already know of a few ways, as I said this is still a prototype). If the latter, I want a clean interface mostly for debugging, what is wrong with that? Besides, using print a couple of times (only when I want printf behaviour) can't cause that much of an impact can it?
Tosi
Member
Member
Posts: 255
Joined: Tue Jun 15, 2010 9:27 am
Location: Flyover State, United States
Contact:

Re: printf + html = ?

Post by Tosi »

felipe wrote:
berkus wrote:Waste of cpu?
The implementation or the idea? If the former how can I improve the code to run faster? (I already know of a few ways, as I said this is still a prototype). If the latter, I want a clean interface mostly for debugging, what is wrong with that?
I would recommend some form of printf. If you want special features like color without ugly escape sequences clogging up your strings, write a separate program that parses your pseudo-HTML and converts the tags to escape sequences to be sent to your console driver.

It's also good to keep the implementation details of print formatting separate from the console driver, since the latter may change greatly in a later version of your OS, while the other is relatively static. It is good to have some kind of abstraction layer between your kernel's print functions and the driver that is to output the text to somewhere.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: printf + html = ?

Post by Solar »

It's also a matter of violating expectations.

Programmers expect a certain behaviour of a function called printf(), which is quite unlike what you scetched at. Use implementations that imply the least surprise to people used to the language.

The following could be done by adding nothing more than a new conversion specifier, %<, to an existing printf() implementation. It also keeps the format string less verbose.

Code: Select all

  printf( "name=%<red>%s%<>, "
          "count=%<blue>%d%<>, "
          "pointer=%<cyan:red>%x\n",
          "felipe", 1234, 0xffffffbc );
I would still advise against it. Adding new conversion specifiers is not standard-compliant. If you need this, make a function of your own, but don't call it printf() (perhaps colored_print() or somesuch), and be prepared that people won't like it much.

And then have a look at freopen(), and ask yourself what happens with your color-printing printf() when someone redirects its output into a file...
Every good solution is obvious once you've found it.
felipe
Member
Member
Posts: 27
Joined: Tue Sep 28, 2010 8:19 am

Re: printf + html = ?

Post by felipe »

Thanks for all the feedback guys.
Solar wrote: Programmers expect a certain behaviour of a function called printf(). If you need this, make a function of your own, but don't call it printf() (perhaps colored_print() or somesuch), and be prepared that people won't like it much.
I did call it print, but perhaps I should have used a more distinguishable name.

I still intend to use this though. The key point here is my intention, I know it this approach is not the most efficient and will break expectations, but I designed this to help me debugging not for the user shell. And I still think that it is clean looking. I never liked the \e[2;3m. syntax, but that is just me also.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: printf + html = ?

Post by Solar »

felipe wrote:I did call it print, but perhaps I should have used a more distinguishable name.
I think you should. Then again, I've always been pedantic with keeping standard-lib API and OS API obviously seperate. (IMHO, OS API should use CamelCase() identifiers just to make it very clear that it's non-standard.)
I know it this approach is not the most efficient and will break expectations, but I designed this to help me debugging not for the user shell.
There's a "me" too many in that sentence. You designed this to help debugging. That doesn't change the rule that APIs shouldn't be surprising, not only for you but also Joe Average. You might not stay alone in your project forever. ;-)
And I still think that it is clean looking.
Well... it's neither printf() nor is it XML. Another "style" thing, but I believe in this: If you roll your own, don't try to make it look like something else which it isn't. Find a syntax that is efficient. As it is, I see some problems trying to figure out why output doesn't look the way you intended, as your checking of the XML-lookalike syntax is rather weak. (No offense intended - but there could be "checkpoints" / assert() calls in the code to faciliate debugging a faulty print-statement.
I never liked the \e[2;3m. syntax, but that is just me also.
There we agree completely. I never could figure out how that syntax came to be.
Every good solution is obvious once you've found it.
felipe
Member
Member
Posts: 27
Joined: Tue Sep 28, 2010 8:19 am

Re: printf + html = ?

Post by felipe »

Solar wrote:You might not stay alone in your project forever.
Good point. Indeed I hope not. =P~
Solar wrote:As it is, I see some problems trying to figure out why output doesn't look the way you intended, as your checking of the XML-lookalike syntax is rather weak. (No offense intended - but there could be "checkpoints" / assert() calls in the code to faciliate debugging a faulty print-statement.
Yes, I had noted that. If you so much as insert a space where you shouldn't the code breaks. But this is expected, my own style might not be the recommended but I often write a whole "module" and only when it is reasonably stable I do a kind of "penetration testing" and add comments. (Perhaps I should have done this before posting the code, but I did warn that it was not done yet.)

I see your main point now. Having a print ( f ) that doesn't behave like a printf or having XML syntax that is not exactly compliant is akin to setting a trap for others using this code. I will see how I can make both the function (easy) and the syntax (hmm?) clearly show that they shouldn't be taken for something else.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: printf + html = ?

Post by bewing »

Solar wrote:
I never liked the \e[2;3m. syntax, but that is just me also.
There we agree completely. I never could figure out how that syntax came to be.
You mean, besides ANSI X3.64?

Before that, it came from either DEC or Tektronix, AFAIK.
Post Reply