summaryrefslogtreecommitdiff
path: root/src/user/knob/file.c
blob: f1a039db6babb7e4592447e65c2b4c371dd480f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <pland/syscall.h>
#include <pland/pcrt.h>
#include <knob/format.h>
#include <knob/heap.h>
#include <knob/env.h>

struct ofl_node {
  struct ofl_node *next;
  struct ofl_node *prev;
  _file_handle_t handle;
};

static struct ofl_node *head_ofl_node = 0;

static void _close_all_files() {
  for (struct ofl_node *i = head_ofl_node; i; i = i->next)
    _close_file(i->handle);
}

BEFORE_QUIT(_close_all_files)

struct file {
  struct ofl_node *node;
  _file_handle_t handle;
  uint32_t position;
  uint32_t length;
};

const char *remove_prefix(const char *path, uint8_t *dn_out) {
  if ((path[0] != 's') || (path[1] != 'd'))
    goto no_prefix;

  const char *num_part = path + 2;
  for (uint32_t i = 0; num_part[i]; ++i)
    if (num_part[i] == ':') {

      uint32_t dn_large;
      if (!try_sntoi(num_part, i, &dn_large) || dn_large > 255)
        goto no_prefix;

      *dn_out = (uint8_t)dn_large;
      return num_part + i + 1;
    }

no_prefix:
  *dn_out = current_drive;
  return path;
}

struct file *open_file(const char *path) {
  uint8_t dn;
  path = remove_prefix(path, &dn);

  _file_handle_t h = _open_file(dn, path);
  if (!h)
    return 0;

  struct ofl_node *new_node = get_block(sizeof(struct ofl_node));
  new_node->next = head_ofl_node;
  new_node->prev = 0;
  new_node->handle = h;
  if (head_ofl_node)
    head_ofl_node->prev = new_node;
  head_ofl_node = new_node;

  struct file *f = get_block(sizeof(struct file));
  f->node = new_node;
  f->handle = h;
  f->position = 0;
  f->length = _file_size(h);

  return f;
}

void close_file(struct file *f) {
  _close_file(f->handle);
  struct ofl_node *n = f->node;
  if (n->next)
    n->next->prev = n->prev;
  if (n->prev)
    n->prev->next = n->next;
  if (n == head_ofl_node)
    head_ofl_node = n->next;
  free_block(n);
  free_block(f);
}

uint32_t read_from_file(struct file *f, uint32_t max, void *buf) {
  if (f->position + max > f->length)
    max = f->length - f->position;

  uint32_t read = _file_read(f->handle, f->position, max, buf);

  f->position += read;
  return read;
}

//return value and max_length don't include null terminator
uint32_t read_line_from_file(struct file *f, char *sz, uint32_t max_length) {
  uint8_t i;
  for (i = 0; i < max_length; ++i) {
    char byte;
    if (!read_from_file(f, 1, &byte) || (byte == '\n'))
      break;
    sz[i] = byte;
  }
  sz[i] = '\0';
  return i;
}

uint32_t seek_file_to(struct file *f, uint32_t to) {
  if (to > f->length)
    to = f->length;
  return f->position = to;
}

int32_t seek_file_by(struct file *f, int32_t by) {
  uint32_t old = f->position;
  uint32_t to = old + by > f->length ? f->length : old + by;
  f->position = to;
  return to - old;
}

__attribute__ ((pure))
uint32_t file_size(struct file *f) {
  return f->length;
}

//return value must be manually freed, unless it is a null pointer
_dir_info_entry_t *get_directory_info(const char *path, uint32_t *count_out) {
  uint8_t dn;
  path = remove_prefix(path, &dn);

  uint32_t count = _count_of_dir(dn, path);
  if (!count) {
    *count_out = 0;
    return 0;
  }

  _dir_info_entry_t *buffer = get_block(count * sizeof(_dir_info_entry_t));
  *count_out = _enumerate_dir(dn, path, buffer, count);
  return buffer;
}