+struct odes *
+task_status_find_part(struct odes *to, char *part, uns create)
+{
+ for (struct oattr *a = obj_find_attr(to, 'P' + OBJ_ATTR_SON); a; a=a->same)
+ {
+ struct odes *o = a->son;
+ char *name = obj_find_aval(o, 'P');
+ ASSERT(name);
+ if (!strcmp(name, part))
+ return o;
+ }
+ if (!create)
+ return NULL;
+ struct odes *o = obj_add_son(to, 'P' + OBJ_ATTR_SON);
+ obj_set_attr(o, 'P', part);
+ return o;
+}
+
+static void
+task_record_history(char *user, char *task, char *part, char *ext, uns version, char *submitted_name)
+{
+ if (!history_format)
+ return;
+
+ time_t now = time(NULL);
+ struct tm *tm = localtime(&now);
+ char prefix[256];
+ if (strftime(prefix, sizeof(prefix), history_format, tm) <= 0)
+ {
+ msg(L_ERROR, "Error formatting history prefix: too long");
+ return;
+ }
+
+ char *name = stk_printf("%s%s:%s:%s:v%d.%s", prefix, user, task, (strcmp(task, part) ? part : (char*)""), version, ext);
+ struct fastbuf *orig = bopen(submitted_name, O_RDONLY, 4096);
+ struct fastbuf *hist = bopen(name, O_WRONLY | O_CREAT | O_EXCL, 4096);
+ bbcopy_slow(orig, hist, ~0U);
+ bclose(hist);
+ bclose(orig);
+}
+
+void
+task_submit_part(char *user, char *task, char *part, char *ext, uns version, struct fastbuf *fb)
+{
+ char *dir = stk_printf("solutions/%s/%s", user, task);
+ char *name = stk_printf("%s/%s.%s", dir, part, ext);
+
+ struct stat st;
+ if (stat(dir, &st) < 0 && errno == ENOENT && mkdir(dir, 0777) < 0)
+ die("Cannot create %s: %m", dir);
+
+ bconfig(fb, BCONFIG_IS_TEMP_FILE, 0);
+ if (rename(fb->name, name) < 0)
+ die("Cannot rename %s to %s: %m", fb->name, name);
+
+ task_record_history(user, task, part, ext, version, name);
+}
+