user: Translate the below assembly code into C programming language:

```asm
<print_string>:
  endbr64
  push   %rbp
  mov    %rsp,%rbp
  sub    $0x30,%rsp
  mov    %rsi,-0x20(%rbp)
  mov    %edx,-0x18(%rbp)
  mov    %ecx,%eax
  mov    %edi,%edx
  mov    %dl,-0x14(%rbp)
  mov    %al,-0x24(%rbp)
  movl   $0x0,-0x4(%rbp)
  movb   $0x0,-0x5(%rbp)
  cmpq   $0x0,-0x20(%rbp)
  jne    L27
  lea    D30(%rip),%rax
  mov    %rax,%rsi
  lea    D17(%rip),%rax
  mov    %rax,%rdi
  mov    $0x0,%eax
  call   <printf@plt>
  mov    $0xffffffff,%edi
  call   <exit@plt>
L27:
  cmpl   $0x0,-0x18(%rbp)
  jns    L70
  cmpb   $0x0,-0x14(%rbp)
  jne    L70
  lea    D41(%rip),%rax
  mov    %rax,%rsi
  lea    D17(%rip),%rax
  mov    %rax,%rdi
  mov    $0x0,%eax
  call   <printf@plt>
  mov    $0xffffffff,%edi
  call   <exit@plt>
L70:
  cmpb   $0x0,-0x24(%rbp)
  je     L55
  movl   $0x0,-0x4(%rbp)
  jmp    L154
L13:
  mov    -0x20(%rbp),%rax
  lea    0x1(%rax),%rdx
  mov    %rdx,-0x20(%rbp)
  movzbl (%rax),%eax
  mov    %al,-0x5(%rbp)
  cmpb   $0x0,-0x5(%rbp)
  jne    L60
  lea    D22(%rip),%rax
  mov    %rax,%rdi
  mov    $0x0,%eax
  call   <printf@plt>
  jmp    L214
L60:
  movzbl -0x5(%rbp),%eax
  mov    %eax,%esi
  lea    D37(%rip),%rax
  mov    %rax,%rdi
  mov    $0x0,%eax
  call   <printf@plt>
L214:
  mov    -0x4(%rbp),%eax
  add    $0x1,%eax
  cmp    %eax,-0x18(%rbp)
  jg     L42
  cmpl   $0x0,-0x18(%rbp)
  jns    L134
L42:
  mov    -0x20(%rbp),%rax
  movzbl (%rax),%eax
  test   %al,%al
  jne    L169
  cmpb   $0x0,-0x14(%rbp)
  je     L134
L169:
  mov    $0x3a,%edi
  call   <putchar@plt>
L134:
  addl   $0x1,-0x4(%rbp)
L154:
  mov    -0x4(%rbp),%eax
  cmp    -0x18(%rbp),%eax
  jl     L3
  cmpl   $0x0,-0x18(%rbp)
  jns    L20
L3:
  mov    -0x20(%rbp),%rax
  movzbl (%rax),%eax
  test   %al,%al
  jne    L13
  cmpb   $0x0,-0x14(%rbp)
  jne    L13
  jmp    L20
L55:
  movl   $0x0,-0x4(%rbp)
  jmp    L35
L101:
  mov    -0x20(%rbp),%rax
  lea    0x1(%rax),%rdx
  mov    %rdx,-0x20(%rbp)
  movzbl (%rax),%eax
  mov    %al,-0x5(%rbp)
  cmpb   $0x0,-0x5(%rbp)
  jne    L146
  mov    $0x2d,%edi
  call   <putchar@plt>
  jmp    L71
L146:
  movzbl -0x5(%rbp),%eax
  mov    %eax,%edi
  call   <putchar@plt>
L71:
  addl   $0x1,-0x4(%rbp)
L35:
  mov    -0x4(%rbp),%eax
  cmp    -0x18(%rbp),%eax
  jl     L41
  cmpl   $0x0,-0x18(%rbp)
  jns    L20
L41:
  mov    -0x20(%rbp),%rax
  movzbl (%rax),%eax
  test   %al,%al
  jne    L101
  cmpb   $0x0,-0x14(%rbp)
  jne    L101
L20:
  nop
  leave
  ret
```

tool_calls: name: extract_symbolic_data
arguments: data_label: D30
data_type: string
name: extract_symbolic_data
arguments: data_label: D17
data_type: string
name: extract_symbolic_data
arguments: data_label: D41
data_type: string
name: extract_symbolic_data
arguments: data_label: D22
data_type: string
name: extract_symbolic_data
arguments: data_label: D37
data_type: string

tool: D30: Null Pointer Exception - print_string

tool: D17: Error:	%s


tool: D41: Invalid Argument Exception - must end with length or null value

tool: D22: --

tool: D37: %02X

assistant: ```c
void print_string(char ignore_nulls_flag, char *str, int len, char hex_flag) {
  int n = 0;
  unsigned char c = 0;
  if (str == 0) {
    printf("Error:\t%s\n", "Null Pointer Exception - print_string");
    exit(-1);
  }
  if ((len < 0 && !ignore_nulls_flag)) {
    printf("Error:\t%s\n", "Invalid Argument Exception - must end with length or null value");
    exit(-1);
  }
  if (hex_flag) {
    for (n = 0; (n < len || len < 0) && (*str || ignore_nulls_flag); n++) {
      c = (unsigned char)(*str++);
      if (c == 0) {
        printf("--");
      } else {
        printf("%02X", c);
      }
      if ((n + 1 < len || len < 0) && (*str || ignore_nulls_flag))
        printf(":");
    }
  } else {
    for (n = 0; (n < len || len < 0) && (*str || ignore_nulls_flag); n++) {
      c = (unsigned char)(*str++);
      if (c == 0) {
        printf("-");
      } else {
        printf("%c", c);
      }
    }
  }
}
```