cmd.c (6704B)
1#include "cmd.h" 2 3#include "data.h" 4#include "list.h" 5#include "player.h" 6#include "ref.h" 7#include "tui.h" 8#include "util.h" 9 10#include <asm-generic/errno-base.h> 11#include <errno.h> 12#include <stdbool.h> 13#include <string.h> 14 15static const struct cmd *last_cmd; 16static char *last_args; 17 18static void cmd_status_from_errno(int err); 19 20static bool cmd_save(const char *args); 21static bool cmd_move(const char *args); 22static bool cmd_copy(const char *args); 23static bool cmd_reindex(const char *args); 24static bool cmd_add_tag(const char *args); 25static bool cmd_rm_tag(const char *args); 26static bool cmd_rename(const char *args); 27 28const struct cmd commands[] = { 29 { "save", cmd_save }, 30 { "move", cmd_move }, 31 { "copy", cmd_copy }, 32 { "reindex", cmd_reindex }, 33 { "addtag", cmd_add_tag }, 34 { "rmtag", cmd_rm_tag }, 35 { "rename", cmd_rename }, 36}; 37 38const size_t command_count = ARRLEN(commands); 39 40void 41cmd_status_from_errno(int err) 42{ 43 if (err == EACCES || err == EPERM) 44 USER_STATUS("Missing permissions"); 45 else if (err == EEXIST) 46 USER_STATUS("Path already exists"); 47 else 48 USER_STATUS("Unknown error"); 49} 50 51bool 52cmd_save(const char *args) 53{ 54 struct list_link *link; 55 struct tag *tag; 56 57 for (LIST_ITER(&tags, link)) { 58 tag = LIST_UPCAST(link, struct tag, link); 59 if (tag->index_dirty || tag->reordered) 60 tag_save_tracks(tag); 61 } 62 63 return true; 64} 65 66bool 67cmd_move(const char *name) 68{ 69 struct list_link *link; 70 struct track *track; 71 struct tag *tag; 72 73 tag = tag_find(name); 74 if (!tag) { 75 USER_STATUS("Tag not found"); 76 return false; 77 } 78 79 link = list_at(tracks_vis, track_nav.sel); 80 if (!link) { 81 USER_STATUS("No track selected"); 82 return false; 83 } 84 track = tracks_vis_track(link); 85 86 if (track->tag == tag) { 87 USER_STATUS("Same tag"); 88 return false; 89 } 90 91 if (!track_move(track, tag)) { 92 cmd_status_from_errno(errno); 93 return false; 94 } 95 96 return true; 97} 98 99bool 100cmd_copy(const char *name) 101{ 102 struct list_link *link; 103 struct track *track, *new; 104 struct tag *tag; 105 char *newpath; 106 107 tag = tag_find(name); 108 if (!tag) { 109 USER_STATUS("Tag not found"); 110 return false; 111 } 112 113 link = list_at(tracks_vis, track_nav.sel); 114 if (!link) { 115 USER_STATUS("No track selected"); 116 return false; 117 } 118 track = tracks_vis_track(link); 119 120 if (track->tag == tag) { 121 USER_STATUS("Same tag"); 122 return false; 123 } 124 125 newpath = aprintf("%s/%s", tag->fpath, track->name); 126 if (path_exists(newpath)) { 127 free(newpath); 128 USER_STATUS("File already exists"); 129 return false; 130 } 131 132 if (!dup_file(track->fpath, newpath)) { 133 free(newpath); 134 USER_STATUS("Failed to copy track"); 135 return false; 136 } 137 free(newpath); 138 139 new = track_add(tag, track->name); 140 if (!new) { 141 rm_file(track->fpath); 142 USER_STATUS("Failed to copy track"); 143 return false; 144 } 145 146 return true; 147} 148 149bool 150cmd_reindex(const char *name) 151{ 152 struct track *track; 153 struct list_link *link; 154 struct tag *tag; 155 struct ref *ref; 156 struct list matches; 157 struct tag *playing_tag; 158 char *playing_name; 159 bool status; 160 161 status = false; 162 playing_tag = NULL; 163 playing_name = NULL; 164 list_init(&matches); 165 166 if (!*name) { 167 link = list_at(&tags, tag_nav.sel); 168 if (!link) return false; 169 tag = LIST_UPCAST(link, struct tag, link); 170 ref = ref_alloc(tag); 171 list_insert_back(&matches, &ref->link); 172 } else if (!strcmp(name, "*")) { 173 for (LIST_ITER(&tags, link)) { 174 tag = LIST_UPCAST(link, struct tag, link); 175 ref = ref_alloc(tag); 176 list_insert_back(&matches, &ref->link); 177 } 178 } else { 179 for (LIST_ITER(&tags, link)) { 180 tag = LIST_UPCAST(link, struct tag, link); 181 if (!strcmp(tag->name, name)) { 182 ref = ref_alloc(tag); 183 list_insert_back(&matches, &ref->link); 184 break; 185 } 186 } 187 } 188 189 if (list_empty(&matches)) 190 return false; 191 192 /* save old playing track */ 193 if (player.track) { 194 playing_tag = player.track->tag; 195 playing_name = astrdup(player.track->name); 196 player.track = NULL; 197 } 198 199 /* update each tag specified */ 200 for (LIST_ITER(&matches, link)) { 201 ref = LIST_UPCAST(link, struct ref, link); 202 if (!tag_reindex_tracks(ref->data)) 203 goto cleanup; 204 } 205 206 /* try to find old playing track among reindexed tracks */ 207 if (playing_tag) { 208 for (LIST_ITER(&playing_tag->tracks, link)) { 209 track = LIST_UPCAST(link, struct track, link_tt); 210 if (!strcmp(track->name, playing_name)) { 211 player.track = track; 212 break; 213 } 214 } 215 } 216 217 status = true; 218 219cleanup: 220 refs_free(&matches); 221 free(playing_name); 222 223 return status; 224} 225 226bool 227cmd_add_tag(const char *name) 228{ 229 struct tag *tag; 230 231 tag = tag_find(name); 232 if (tag) { 233 USER_STATUS("Tag already exists"); 234 return false; 235 } 236 237 tag = tag_create(name); 238 if (!tag) { 239 USER_STATUS("Failed to create tag"); 240 return false; 241 } 242 243 return true; 244} 245 246bool 247cmd_rm_tag(const char *name) 248{ 249 struct list_link *link; 250 struct tag *tag; 251 252 if (!*name) { 253 link = list_at(&tags, tag_nav.sel); 254 if (!link) return false; 255 tag = LIST_UPCAST(link, struct tag, link); 256 } else { 257 tag = tag_find(name); 258 if (!tag) { 259 USER_STATUS("No such tag"); 260 return false; 261 } 262 } 263 264 if (!tag_rm(tag, true)) { 265 USER_STATUS("Failed to remove tag"); 266 return false; 267 } 268 269 return true; 270} 271 272bool 273cmd_rename(const char *name) 274{ 275 struct list_link *link; 276 struct track *track; 277 struct tag *tag; 278 279 if (!*name) { 280 USER_STATUS("Supply a name"); 281 return false; 282 } 283 284 ASSERT(pane_sel == cmd_pane); 285 286 if (pane_after_cmd == track_pane) { 287 link = list_at(tracks_vis, track_nav.sel); 288 if (!link) return false; 289 track = tracks_vis_track(link); 290 if (!track_rename(track, name)) 291 return false; 292 } else if (pane_after_cmd == tag_pane) { 293 link = list_at(&tags, tag_nav.sel); 294 if (!link) return false; 295 tag = LIST_UPCAST(link, struct tag, link); 296 if (!tag_rename(tag, name)) 297 return false; 298 } 299 300 return true; 301} 302 303void 304cmd_init(void) 305{ 306 last_cmd = NULL; 307 last_args = NULL; 308} 309 310void 311cmd_deinit(void) 312{ 313 free(last_args); 314} 315 316bool 317cmd_run(const char *query, bool *found) 318{ 319 const char *sep, *args; 320 int i, cmdlen; 321 322 *found = false; 323 sep = strchr(query, ' '); 324 cmdlen = sep ? sep - query : strlen(query); 325 for (i = 0; i < command_count; i++) { 326 if (!strncmp(commands[i].name, query, cmdlen)) { 327 last_cmd = &commands[i]; 328 args = sep ? sep + 1 : ""; 329 330 free(last_args); 331 last_args = astrdup(args); 332 333 *found = true; 334 return commands[i].func(args); 335 } 336 } 337 338 return false; 339} 340 341bool 342cmd_rerun(void) 343{ 344 if (!last_cmd || !last_args) { 345 USER_STATUS("No command to repeat"); 346 return false; 347 } 348 return last_cmd->func(last_args); 349} 350 351const struct cmd * 352cmd_get(const char *name) 353{ 354 int i; 355 356 for (i = 0; i < command_count; i++) { 357 if (!strcmp(commands[i].name, name)) 358 return &commands[i]; 359 } 360 361 return NULL; 362} 363 364const struct cmd * 365cmd_find(const char *name) 366{ 367 int i; 368 369 for (i = 0; i < command_count; i++) { 370 if (strcmp(commands[i].name, name)) 371 return &commands[i]; 372 } 373 374 return NULL; 375}