pit help; 'none' to reset date or time; show for projects and tasks;

compiles on Ubuntu
This commit is contained in:
Mike Dvorkin
2010-08-11 20:59:08 -07:00
parent 346b35bf2d
commit df4105774a
11 changed files with 257 additions and 52 deletions

View File

@@ -95,6 +95,8 @@ time_t pit_arg_date(char **arg, char *required)
if (required && (!*arg || pit_arg_is_option(arg))) {
die("missing %s", required);
} else if (!strcmp(*arg, "none")) { /* Drop dat value */
return -1;
} else {
bool alpha_date = isalpha(**arg);
bool slash_date = (strchr(*arg, '/') != NULL);
@@ -169,6 +171,8 @@ time_t pit_arg_time(char **arg, char *required)
if (required && (!*arg || pit_arg_is_option(arg))) {
die("missing %s", required);
} else if (!strcmp(*arg, "none")) { /* Drop time value */
return -1;
} else { /* Suppored time formats are :MM, HH:MM, and HH */
char *colon = strchr(*arg, ':');
if (colon == *arg) { /* :MM - minutes only */

View File

@@ -65,7 +65,7 @@ void pit_init(char *argv[]) {
if (!access(file_name, F_OK)) {
if (!*arg || strcmp(*arg, "-f")) { /* Do not prompt user if forced init (-f). */
printf("%s already exist, do you want to override it [y/N]: ", file_name);
printf("%s already exists, do you want to override it [y/N]: ", file_name);
reply = getchar();
}
}
@@ -80,8 +80,8 @@ void pit_info(char *argv[])
{
pit_db_load();
printf("Pit file name: %s\n", pit_file_name());
printf("Created by: %s on %s", header->created_by, ctime(&header->created_at));
printf("Last updated by: %s on %s", header->updated_by, ctime(&header->updated_at));
printf("Created by: %s on %s\n", header->created_by, format_timestamp(header->created_at));
printf("Last updated by: %s on %s\n", header->updated_by, format_timestamp(header->updated_at));
printf("Schema version: %d\n", header->schema_version);
printf("Projects: %d\n", projects->number_of_records);
printf("Tasks: %d\n", tasks->number_of_records);

View File

@@ -3,7 +3,172 @@
#include <stdio.h>
#include "pit.h"
static void usage()
{
char *msg[] = {
"usage: pit command [args]\n",
"The commands are:",
" init Create empty pit database or reinitialize an existing one",
" project Create, search, and manage pit projects",
" task Create, search, and manage pit tasks",
" note Create, search, and manage pit notes",
" log Show chronological pit activity log",
" info Show summary information about your pit database",
" help Show help information about pit",
" version Show pit version number\n",
"All commands might be shortened as long as they remain unambiguous. See 'pit help <command>' for more",
"information on a specific command.\n", NULL
};
printa(msg);
}
static void help_project()
{
char *msg[] = {
"Pit project is basic entity used to group related tasks together. In pit project has name and status, neither",
"of which have any semantical meaning and are just arbitrary strings.\n",
"Creating a project:\n",
" $ pit project -c name [-s status]\n",
"Editing a project:\n",
" $ pit project -e [number] [-n name] [-s status]\n",
"Deleting a project:\n",
" $ pit project -d [number]\n",
"Viewing a project:\n",
" $ pit project [[-q] number]\n",
"Searching projects:\n",
" $ pit project -q [number | [-n name] [-s status]]\n",
"Examples:\n",
" $ pit project -c 'My Tasks'",
" created project 1: My Tasks (status: active)\n",
" $ pit project -c 'Feature Requests' -s backlog",
" created project 2: Feature Requests (status: backlog)\n",
" $ pit pro",
" 1: (username) [active ] My Tasks (0 tasks)",
" * 2: (username) [backlog] Feature Requests (0 tasks)\n",
" $ pit p 1 -e -n 'Task and Bugs' -s current",
" 1: Task and Bugs (current, 0 tasks)\n",
" $ pit p",
" 1: (username) [current] Task and Bugs (0 tasks)",
" * 2: (username) [backlog] Feature Requests (0 tasks)\n",
" $ pit p -d",
" deleted project 2: Feature Requests\n",
" $ pit p",
" * 1: (username) [current] Task and Bugs (0 tasks)\n", NULL
};
printa(msg);
}
static void help_task()
{
char *msg[] = {
"In pit a task belongs to particular project. A task has name, status, priority, date, and time. All attributes",
"except the task name are optional.\n",
"Creating a task:\n",
" pit task -c name [-s status] [-p priority] [-d date] [-t time]\n",
"Editing a task:\n",
" pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time]\n",
"Deleting a task:\n",
" pit task -d [number]\n",
"Viewing a task:\n",
" pit task [[-q] number]\n",
"Searching tasks:\n",
" pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-t time]]\n",
"Supported date formats:\n",
" none, 4/26, 4/26/2012, 4/26/12, '4/26 3pm', '4/26 19:30', '4/26/2012 3:15am', '4/26/12 17:00'",
" 'Apr 26', 'Apr 26, 2012', 'Apr 26 3pm', 'Apr 26 19:30', 'Apr 26, 12 3:15am', 'Apr 26, 2012 17:00'\n",
"Supported time formats:\n",
" none, 17, 17:00, 17:30, 5pm, 1:15am\n",
"Examples:\n",
" $ pit task -c 'Hack this'",
" created task 1/1: Hack this (status: open, priority: normal)\n",
" $ pit task -c 'And hack that' -s new -p urgent -d 'Dec 31'",
" created task 2/1: And hack that (status: new, priority: urgent, date: Dec 31, 2010)\n",
" $ pit t",
" 1: (username) [open] [normal] Hack this (0 notes)",
" * 2: (username) [new ] [urgent] Dec 31, 2010 And hack that (0 notes)\n",
" $ pit t -e 1 -s done -d 10/10 -t 4:30",
" updated task 1: Hack that (status: done, date: Oct 10, 2010, time: 4:30)\n",
" $ pit t",
" 1: (username) [done] [normal] Oct 10, 2010 4:30 Hack this (0 notes)",
" * 2: (username) [new] [urgent] Dec 31, 2010 And hack that (0 notes)\n",
" $ pit t -d",
" deleted task 2: And hack that\n",
" $ pit t",
" 1: (username) [done] [normal] Oct 10, 2010 4:30 Hack this (0 notes)\n", NULL
};
printa(msg);
}
static void help_note()
{
puts("pit note is not implemented yet.");
}
static void help_log()
{
char *msg[] = {
"Show summary information about your pit database. This command is as simple as:\n",
" pit log\n", NULL
};
printa(msg);
}
static void help_init()
{
char *msg[] = {
"Create empty pit database or reinitialize an existing one. Default file name for the pit database",
"is ~/.pit -- you can override the default by setting PITFILE environment variable.\n",
" $ pit init [-f]\n",
" -f force initialization without prompt\n",
"Example:\n",
" $ pit init",
" /home/user/.pit already exists, do you want to override it [y/N]: y",
" Created empty /home/user/.pit\n", NULL
};
printa(msg);
}
static void help_info()
{
char *msg[] = {
"Show summary information about your pit database. This command is as simple as:\n",
" pit info\n", NULL
};
printa(msg);
}
static void help_version()
{
puts("No kidding :-)");
pit_version();
}
void pit_help(char *argv[])
{
puts("pit: help is not implemented yet");
char *command[] = { "project", "task", "note", "log", "init", "info", "help", "version" };
void (*handler[])() = { help_project, help_task, help_note, help_log, help_init, help_info, usage, help_version };
if (!argv[1]) {
usage();
} else {
register int i, candidate = -1;
for(i = 0; i < ARRAY_SIZE(command); i++) {
if (strstr(command[i], argv[1]) == command[i]) {
if (candidate < 0) {
candidate = i;
} else {
printf("no help, <%s> is a bit ambiguous\n", argv[1]);
usage();
}
}
}
if (candidate < 0) {
printf("no help, <%s> is unknown command\n", argv[1]);
usage();
} else {
handler[candidate]();
}
}
}

View File

@@ -16,8 +16,6 @@ typedef struct _Project {
char name[128]; /* Project name. */
char status[16]; /* Project status. */
int number_of_tasks; /* Number of tasks for the project. */
int created_by; /* Who created the project? */
int updated_by; /* Who last updated the project? */
time_t created_at; /* When the project was created? */
time_t updated_at; /* When the project was last updated? */
} Project, *PProject;

View File

@@ -45,7 +45,7 @@ static void print_tasks(PPager ppager)
char **pentry;
char format[64];
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds (%%d note%%s)\n",
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds (%%d note%%s)\n",
ppager->max.task.id, ppager->max.task.username, ppager->max.task.status, ppager->max.task.priority, ppager->max.task.name
);
for_each_entry(ppager, pentry) {
@@ -67,7 +67,7 @@ static void print_tasks_with_date(PPager ppager)
char **pentry;
char format[64];
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds %%-%ds (%%d note%%s)\n",
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds %%-%ds (%%d note%%s)\n",
ppager->max.task.id, ppager->max.task.username, ppager->max.task.status, ppager->max.task.priority, ppager->max.task.date, ppager->max.task.name
);
for_each_entry(ppager, pentry) {
@@ -90,7 +90,7 @@ static void print_tasks_with_time(PPager ppager)
char **pentry;
char format[64];
sprintf(format, "%%c %%%dd: (%%-%ds) %%-%ds %%-%ds %%%ds %%-%ds (%%d note%%s)\n",
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%%ds %%-%ds (%%d note%%s)\n",
ppager->max.task.id, ppager->max.task.username, ppager->max.task.status, ppager->max.task.priority, ppager->max.task.time, ppager->max.task.name
);
for_each_entry(ppager, pentry) {
@@ -113,7 +113,7 @@ static void print_tasks_with_date_and_time(PPager ppager)
char **pentry;
char format[64];
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds %%%ds %%-%ds (%%d note%%s)\n",
sprintf(format, "%%c %%%dd: (%%-%ds) [%%-%ds] [%%-%ds] %%-%ds %%%ds %%-%ds (%%d note%%s)\n",
ppager->max.task.id, ppager->max.task.username, ppager->max.task.status, ppager->max.task.priority, ppager->max.task.date, ppager->max.task.time, ppager->max.task.name
);
for_each_entry(ppager, pentry) {

View File

@@ -61,7 +61,8 @@ int main(int argc, char *argv[]) {
char *command[] = { "project", "task", "note", "log", "init", "info", "help", "version" };
void (*handler[])(char *argv[]) = { pit_project, pit_task, pit_note, pit_log, pit_init, pit_info, pit_help, pit_version };
if (argc == 1) argv[1] = "help";
if (argc == 1) { argv[1] = "help"; argv[2] = NULL; }
for(i = 0; i < ARRAY_SIZE(command); i++) {
if (strstr(command[i], argv[1]) == command[i]) {
if (candidate < 0) {

View File

@@ -5,7 +5,8 @@
typedef int bool;
#include <time.h>
#define __USE_XOPEN
#include <time.h> /* __USE_XOPEN needed for strptime() */
#include "object.h"
#include "table.h"
#include "pager.h"
@@ -47,8 +48,9 @@ void pit_project(char *argv[]);
void pit_task(char *argv[]);
void pit_note(char *argv[]);
void pit_log(char *argv[]);
void pit_status(char *argv[]);
void pit_info(char *argv[]);
void pit_help(char *argv[]);
void pit_version();
void pit_db_load();
void pit_db_save();
void pit_db_initialize();
@@ -70,12 +72,14 @@ void die(char *msg, ...);
void perish(char *prefix);
char *str2str(char *str);
char *mem2str(char *mem, int len);
bool zero(char *mem, int len);
bool is_zero(char *mem, int len);
void printa(char *msg[]);
char *current_user();
char *home_dir(char *username, int len);
char *expand_path(char *path, char *expanded);
char *format_date(time_t date);
char *format_time(time_t time);
char *format_timestamp(time_t timestamp);
char *inline_replace(char *this, char *old, char *new);
#endif

View File

@@ -46,6 +46,23 @@ static void project_list(POptions po)
}
}
static void project_show(int id)
{
PProject pp;
pit_db_load();
id = project_find_current(id, &pp);
if (pp) {
printf("* %d: (%s) %s (status: %s, %d task%s)\n", pp->id, pp->username, pp->name, pp->status, pp->number_of_tasks, (pp->number_of_tasks != 1 ? "s" : ""));
printf("The project was created on %s, last updated on %s\n", format_timestamp(pp->created_at), format_timestamp(pp->updated_at));
pit_table_mark(projects, pp->id);
pit_db_save();
} else {
die("could not find the project");
}
}
static void project_create(POptions po)
{
pit_db_load();
@@ -71,28 +88,6 @@ static void project_create(POptions po)
}
}
static void project_show(int id)
{
PProject pp;
pit_db_load();
pp = (PProject)pit_table_find(projects, id);
if (pp) {
printf("%d: %s (%s, %d task%s)\n",
pp->id, pp->name, pp->status, pp->number_of_tasks, (pp->number_of_tasks != 1 ? "s" : ""));
if (pp->number_of_tasks > 0) {
puts("Tasks:");
for_each_task(pt) {
printf(" %c %d: %s (%d notes)\n", (pt->id == tasks->current ? '*' : ' '), pt->id, pt->name, pt->number_of_notes);
}
}
pit_table_mark(projects, pp->id);
pit_db_save();
} else {
die("could not find the project");
}
}
static void project_update(int id, POptions po)
{
PProject pp;
@@ -246,7 +241,7 @@ void pit_project(char *argv[])
number = pit_arg_number(++arg, NULL);
if (!number) --arg;
project_parse_options(arg, &opt);
if (zero((char *)&opt.project, sizeof(opt.project))) {
if (is_zero((char *)&opt.project, sizeof(opt.project))) {
die("nothing to update");
} else {
project_update(number, &opt);
@@ -262,7 +257,7 @@ void pit_project(char *argv[])
project_show(number);
} else {
project_parse_options(--arg, &opt);
if (zero((char *)&opt.project, sizeof(opt.project))) {
if (is_zero((char *)&opt.project, sizeof(opt.project))) {
project_show(0); /* Show current project if any. */
} else {
project_list(&opt);

View File

@@ -146,7 +146,7 @@ char *pit_table_delete(PTable pt, int id) {
*/
char *pit_table_insert(PTable pt, char *record) {
register char **pi;
register time_t now;
register time_t now = time(NULL);
if (pt->number_of_records >= pt->number_of_slots) {
pt = table_extend(pt);
@@ -170,7 +170,6 @@ char *pit_table_insert(PTable pt, char *record) {
** fields of type "time_t".
*/
if (pt->flags & TABLE_HAS_CREATED_AT || pt->flags & TABLE_HAS_UPDATED_AT) {
now = time(NULL);
*(time_t *)(*pi + pt->record_size - sizeof(time_t)) = now;
}
if (pt->flags & TABLE_HAS_CREATED_AT && pt->flags & TABLE_HAS_UPDATED_AT) {

View File

@@ -49,7 +49,22 @@ static void task_list(POptions po)
static void task_show(int id)
{
printf("task_show(%d)\n", id);
PTask pt;
pit_db_load();
id = task_find_current(id, &pt);
if (pt) {
printf("* %d: (%s) %s (project: %d, status: %s, priority: %s", pt->id, pt->username, pt->name, pt->project_id, pt->status, pt->priority);
if (pt->date) printf(", date: %s", format_date(pt->date));
if (pt->time) printf(", time: %s", format_time(pt->time));
printf(", %d note%s)\n", pt->number_of_notes, pt->number_of_notes == 1 ? "" : "s");
printf("The task was created on %s, last updated on %s\n", format_timestamp(pt->created_at), format_timestamp(pt->updated_at));
pit_table_mark(tasks, pt->id);
pit_db_save();
} else {
die("could not find the task");
}
}
static void task_create(POptions po)
@@ -72,8 +87,8 @@ static void task_create(POptions po)
strncpy(t.priority, po->task.priority, sizeof(t.priority) - 1);
strncpy(t.username, current_user(), sizeof(t.username) - 1);
t.project_id = pp->id;
t.date = po->task.date;
t.time = po->task.time;
t.date = max(0, po->task.date);
t.time = max(0, po->task.time);
pt = (PTask)pit_table_insert(tasks, (char *)&t);
pit_table_mark(tasks, pt->id);
@@ -93,8 +108,8 @@ static void task_update(int id, POptions po)
if (po->task.name) strncpy(pt->name, po->task.name, sizeof(pt->name) - 1);
if (po->task.status) strncpy(pt->status, po->task.status, sizeof(pt->status) - 1);
if (po->task.priority) strncpy(pt->priority, po->task.priority, sizeof(pt->priority) - 1);
if (po->task.date) pt->date = po->task.date;
if (po->task.time) pt->time = po->task.time;
if (po->task.date) pt->date = max(0, po->task.date);
if (po->task.time) pt->time = max(0, po->task.time);
pit_table_mark(tasks, pt->id);
task_log_update(pt, po);
@@ -168,8 +183,8 @@ static void task_log_create(PTask pt, POptions po)
char str[256];
sprintf(str, "created task %d: %s (status: %s, priority: %s", pt->id, po->task.name, po->task.status, po->task.priority);
if (po->task.date) sprintf(str + strlen(str), ", date: %s", format_date(po->task.date));
if (po->task.time) sprintf(str + strlen(str), ", time: %s", format_time(po->task.time));
if (po->task.date > 0) sprintf(str + strlen(str), ", date: %s", format_date(po->task.date));
if (po->task.time > 0) sprintf(str + strlen(str), ", time: %s", format_time(po->task.time));
strcat(str, ")");
puts(str);
pit_action(pt->id, "task", str);
@@ -196,10 +211,20 @@ static void task_log_update(PTask pt, POptions po)
empty = FALSE;
}
if (po->task.date) {
sprintf(str + strlen(str), "%sdate: %s", (empty ? "" : ", "), format_date(po->task.date));
if (po->task.date < 0) {
sprintf(str + strlen(str), "%sdate: none", (empty ? "" : ", "));
} else {
sprintf(str + strlen(str), "%sdate: %s", (empty ? "" : ", "), format_date(po->task.date));
}
empty = FALSE;
}
if (po->task.time) sprintf(str + strlen(str), "%stime: %s", (empty ? "" : ", "), format_time(po->task.time));
if (po->task.time) {
if (po->task.time < 0) {
sprintf(str + strlen(str), "%stime: none", (empty ? "" : ", "));
} else {
sprintf(str + strlen(str), "%stime: %s", (empty ? "" : ", "), format_time(po->task.time));
}
}
strcat(str, ")");
puts(str);
pit_action(pt->id, "task", str);
@@ -266,7 +291,7 @@ void pit_task(char *argv[])
number = pit_arg_number(++arg, NULL);
if (!number) --arg;
task_parse_options(arg, &opt);
if (!opt.task.name && !opt.task.status && !opt.task.priority && !opt.task.date && !opt.task.time) {
if (is_zero((char *)&opt.task, sizeof(opt.task))) {
die("nothing to update");
} else {
task_update(number, &opt);
@@ -282,7 +307,7 @@ void pit_task(char *argv[])
task_show(opt.task.id);
} else {
task_parse_options(--arg, &opt);
if (!opt.task.name && !opt.task.status && !opt.task.priority && !opt.task.date && !opt.task.time) {
if (is_zero((char *)&opt.task, sizeof(opt.task))) {
task_show(0); /* Show current task if any. */
} else {
task_list(&opt);

View File

@@ -18,7 +18,7 @@ char *mem2str(char *mem, int len) {
return str;
}
bool zero(char *mem, int len) {
bool is_zero(char *mem, int len) {
char *pch = mem;
while(pch - mem < len) {
@@ -27,6 +27,10 @@ bool zero(char *mem, int len) {
return TRUE;
}
void printa(char *msg[]) {
while(*msg) puts(*msg++);
}
char *current_user() {
static char *username = NULL;
@@ -99,6 +103,16 @@ char *format_time(time_t time)
return str;
}
char *format_timestamp(time_t timestamp)
{
static char str[32];
struct tm *ptm = localtime(&timestamp);
strftime(str, sizeof(str), "%b %d, %Y at %H:%M", ptm);
return str;
}
char *inline_replace(char *this, char *old, char *new)
{
char *start = this;