player.c (3103B)
1 /* 2 * Copyright 2021, 2022 Jacob R. Edwards 3 * 4 * ap -- audio player 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 #include <sys/wait.h> 21 22 #include <errno.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "arg.h" 30 #include "player.h" 31 32 int 33 pstart(struct player *p) 34 { 35 char *prog; 36 37 if (p->state & (RUNNING | SUSPENDED)) { 38 errno = EALREADY; 39 return 1; 40 } 41 42 p->pid = fork(); 43 if (p->pid < 0) 44 return 1; 45 if (p->pid > 0) { 46 p->state = RUNNING; 47 return 0; 48 } 49 50 prog = p->argv[0]; 51 p->argv[0] = "apsplayer"; 52 p->argv[p->argc] = p->path; 53 54 execvp(prog, p->argv); 55 perror(prog); 56 _exit(1); 57 } 58 59 int 60 pstop(struct player *p) 61 { 62 if (p->state == STOPPED) { 63 errno = EALREADY; 64 return 1; 65 } 66 if (p->state == SUSPENDED) 67 pcontinue(p); 68 if (p->state == RUNNING) 69 kill(p->pid, SIGTERM); /* errors unconsequential */ 70 p->state = STOPPED; 71 return 0; 72 } 73 74 int 75 psuspend(struct player *p) 76 { 77 if (p->state != RUNNING) { 78 errno = EINVAL; 79 return 1; 80 } 81 82 if (kill(p->pid, SIGSTOP) == -1) 83 return 1; 84 p->state = SUSPENDED; 85 return 0; 86 } 87 88 int 89 pcontinue(struct player *p) 90 { 91 if (p->state != SUSPENDED) { 92 errno = EINVAL; 93 return 1; 94 } 95 96 if (kill(p->pid, SIGCONT) == -1) 97 return 1; 98 p->state = RUNNING; 99 return 0; 100 } 101 102 int 103 pplay(struct player *p, char *path) 104 { 105 if (path == NULL && p->path == NULL) { 106 errno = ENOENT; 107 return 1; 108 } 109 110 if (path == NULL || (p->path != NULL && strcmp(p->path, path) == 0)) { 111 if (p->state == SUSPENDED) 112 return pcontinue(p); 113 return pstart(p); 114 } 115 116 if (p->state & (SUSPENDED | RUNNING) && pstop(p)) 117 return 1; 118 119 free(p->path); 120 p->path = strdup(path); 121 if (p->path == NULL) 122 return 1; 123 124 return pstart(p); 125 } 126 127 int 128 ptoggle(struct player *p) 129 { 130 if (p->state == RUNNING) 131 return psuspend(p); 132 else 133 return pplay(p, NULL); 134 } 135 136 void 137 pupdate(struct player *p, int status) 138 { 139 if (WIFEXITED(status)) { 140 p->state = (WEXITSTATUS(status) ? FAILURE : COMPLETE); 141 } else if (WIFSIGNALED(status)) { 142 p->state = STOPPED; 143 } else if (WIFCONTINUED(status)) { 144 p->state = RUNNING; 145 } else if (WIFSTOPPED(status)) { 146 p->state = SUSPENDED; 147 } 148 } 149 150 void 151 pfree(struct player *p) 152 { 153 if (p == NULL) 154 return; 155 if (p->state != STOPPED) 156 pstop(p); 157 argfree(p->argv); 158 free(p->path); 159 free(p); 160 } 161 162 struct player * 163 pnew(char **argv) 164 { 165 struct player *p; 166 167 p = calloc(1, sizeof(*p)); 168 if (p == NULL) 169 return p; 170 171 p->argc = arglen(argv); 172 p->argv = argdup(argv, p->argc + 1); 173 if (p->argv == NULL) { 174 pfree(p); 175 return NULL; 176 } 177 return p; 178 }