2004 by Marc J. Rochkind. All rights reserved. Portions marked "Open Source" may be copied under license.

 

Main Page   Modules   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

c4/record.c

Go to the documentation of this file.
00001 /*
00002     Record and Playback Example
00003     AUP2, Sec. 4.10.2
00004 
00005     Copyright 2003 by Marc J. Rochkind. All rights reserved.
00006     May be copied only for purposes and under conditions described
00007     on the Web page www.basepath.com/aup/copyright.htm.
00008 
00009     The Example Files are provided "as is," without any warranty;
00010     without even the implied warranty of merchantability or fitness
00011     for a particular purpose. The author and his publisher are not
00012     responsible for any damages, direct or incidental, resulting
00013     from the use or non-use of these Example Files.
00014 
00015     The Example Files may contain defects, and some contain deliberate
00016     coding mistakes that were included for educational reasons.
00017     You are responsible for determining if and how the Example Files
00018     are to be used.
00019 
00020 */
00021 #include "defs.h"
00022 #include "ptutil.h"
00023 #include "tc_setraw.h"
00024 #include <sys/time.h>
00025 #include <termios.h>
00026 
00027 /*
00028     Only works if x >= y.
00029 */
00030 /*[timeval_subtract]*/
00031 static void timeval_subtract(const struct timeval *x,
00032   const struct timeval *y, struct timeval *diff)
00033 {
00034     if (x->tv_sec == y->tv_sec || x->tv_usec >= y->tv_usec) {
00035         diff->tv_sec = x->tv_sec - y->tv_sec;
00036         diff->tv_usec = x->tv_usec - y->tv_usec;
00037     }
00038     else {
00039         diff->tv_sec = x->tv_sec - 1 - y->tv_sec;
00040         diff->tv_usec = 1000000 + x->tv_usec - y->tv_usec;
00041     }
00042 }
00043 /*[get_rel_time]*/
00044 static void get_rel_time(struct timeval *tv_rel)
00045 {
00046     static bool first = true;
00047     static struct timeval starttime;
00048     struct timeval tv;
00049 
00050     if (first) {
00051         first = false;
00052         (void)gettimeofday(&starttime, NULL);
00053     }
00054     (void)gettimeofday(&tv, NULL);
00055     timeval_subtract(&tv, &starttime, tv_rel);
00056 }
00057 /*[event-struct]*/
00058 #define EVFILE "recording.tmp"
00059 #define EVBUFSIZE 512
00060 
00061 struct event {
00062     struct timeval e_time;
00063     unsigned e_datalen;
00064 };
00065 static int fd_ev = -1;
00066 /*[ev_open]*/
00067 static bool ev_open(void)
00068 {
00069     ec_neg1( fd_ev = open(EVFILE, O_RDONLY) )
00070     return true;
00071 
00072 EC_CLEANUP_BGN
00073     return false;
00074 EC_CLEANUP_END
00075 }
00076 /*[ev_creat]*/
00077 static bool ev_creat(void)
00078 {
00079     ec_neg1( fd_ev = open(EVFILE, O_WRONLY | O_CREAT | O_TRUNC,
00080       PERM_FILE) )
00081     return true;
00082 
00083 EC_CLEANUP_BGN
00084     return false;
00085 EC_CLEANUP_END
00086 }
00087 /*[ev_write]*/
00088 static bool ev_write(char *data, unsigned datalen)
00089 {
00090     struct event ev = { { 0 } };
00091 
00092     get_rel_time(&ev.e_time);
00093     ev.e_datalen = datalen;
00094     ec_neg1( writeall(fd_ev, &ev, sizeof(ev)) )
00095     ec_neg1( writeall(fd_ev, data, datalen) )
00096     return true;
00097 
00098 EC_CLEANUP_BGN
00099     return false;
00100 EC_CLEANUP_END
00101 }
00102 /*[ev_read]*/
00103 static bool ev_read(struct event *ev, char *data, unsigned datalen)
00104 {
00105     ssize_t nread;
00106 
00107     ec_neg1( nread = read(fd_ev, ev, sizeof(*ev)) )
00108     if (nread != sizeof(*ev)) {
00109         errno = EIO;
00110         EC_FAIL
00111     }
00112     ec_neg1( nread = read(fd_ev, data, ev->e_datalen) )
00113     if (nread != ev->e_datalen) {
00114         errno = EIO;
00115         EC_FAIL
00116     }
00117     return true;
00118 
00119 EC_CLEANUP_BGN
00120     return false;
00121 EC_CLEANUP_END
00122 }
00123 /*[ev_close]*/
00124 static void ev_close(void)
00125 {
00126     (void)close(fd_ev);
00127     fd_ev = -1;
00128 }
00129 /*[ev_sleep]*/
00130 static bool ev_sleep(struct timeval *tv)
00131 {
00132     struct timeval tv_rel, tv_diff;
00133 
00134     get_rel_time(&tv_rel);
00135     if (tv->tv_sec > tv_rel.tv_sec || (tv->tv_sec == tv_rel.tv_sec && tv->tv_usec >= tv_rel.tv_usec)) {
00136         timeval_subtract(tv, &tv_rel, &tv_diff);
00137         (void)sleep(tv_diff.tv_sec);
00138         ec_neg1( usleep(tv_diff.tv_usec) )
00139     }
00140     /* else we are already running late */
00141     return true;
00142 
00143 EC_CLEANUP_BGN
00144     return false;
00145 EC_CLEANUP_END
00146 }
00147 /*[playback]*/
00148 static bool playback(void)
00149 {
00150     bool ok = false;
00151     struct event ev;
00152     char buf[EVBUFSIZE];
00153     struct termios tbuf, tbufsave;
00154 
00155     ec_neg1( tcgetattr(STDIN_FILENO, &tbuf) )
00156     tbufsave = tbuf;
00157     tbuf.c_lflag &= ~ECHO;
00158     ec_neg1( tcsetattr(STDIN_FILENO, TCSAFLUSH, &tbuf) )
00159     ec_false( ev_open() )
00160     while (true) {
00161         ec_false( ev_read(&ev, buf, sizeof(buf)) )
00162         if (ev.e_datalen == 0)
00163             break;
00164         ev_sleep(&ev.e_time);
00165         ec_neg1( writeall(STDOUT_FILENO, buf, ev.e_datalen) )
00166     }
00167     ec_neg1( write(STDOUT_FILENO, "\n", 1) )
00168     ok = true;
00169     EC_CLEANUP
00170 
00171 EC_CLEANUP_BGN
00172     (void)tcdrain(STDOUT_FILENO);
00173     (void)sleep(1); /* Give the terminal a chance to respond. */
00174     (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tbufsave);
00175     ev_close();
00176     return ok;
00177 EC_CLEANUP_END
00178 }
00179 /*[]*/
00180 /*
00181     Following function won't be explained until Chap. 6.
00182 */
00183 /*[exec_redirected]*/
00184 bool exec_redirected(const char *file, char *const argv[], int fd_stdin,
00185   int fd_stdout, int fd_stderr)
00186 {
00187     if (fd_stdin != STDIN_FILENO)
00188         ec_neg1( dup2(fd_stdin, STDIN_FILENO) )
00189     if (fd_stdout != STDOUT_FILENO)
00190         ec_neg1( dup2(fd_stdout, STDOUT_FILENO) )
00191     if (fd_stderr != STDERR_FILENO)
00192         ec_neg1( dup2(fd_stderr, STDERR_FILENO) )
00193     if (fd_stdin != STDIN_FILENO)
00194         (void)close(fd_stdin);
00195     if (fd_stdout != STDOUT_FILENO)
00196         (void)close(fd_stdout);
00197     if (fd_stderr != STDERR_FILENO)
00198         (void)close(fd_stderr);
00199     ec_neg1( execvp(file, argv) )
00200 
00201 EC_CLEANUP_BGN
00202     return false;
00203 EC_CLEANUP_END
00204 }
00205 /*[main-1]*/
00206 int main(int argc, char *argv[])
00207 {
00208     bool ok = false;
00209     PTINFO *p = NULL;
00210 
00211     if (argc < 2) {
00212         fprintf(stderr, "Usage: record cmd ...\n       record -p\n");
00213         exit(EXIT_FAILURE);
00214     }
00215     if (strcmp(argv[1], "-p") == 0) {
00216         playback();
00217         ok = true;
00218         EC_CLEANUP
00219     }
00220 /*[main-2]*/
00221     ec_null( p = pt_open_master() )
00222     switch (fork()) {
00223     case 0:
00224         ec_false( pt_open_slave(p) )
00225         ec_false( exec_redirected(argv[1], &argv[1], PT_GET_SLAVE_FD(p),
00226           PT_GET_SLAVE_FD(p), PT_GET_SLAVE_FD(p)) )
00227         break;
00228     case -1:
00229         EC_FAIL
00230     }
00231     ec_false( ev_creat() )
00232     ec_false( pt_wait_master(p) )
00233 /*[main-3]*/
00234     tc_setraw();
00235     while (true) {
00236         fd_set fd_set_read;
00237         char buf[EVBUFSIZE];
00238         ssize_t nread;
00239 
00240         FD_ZERO(&fd_set_read);
00241         FD_SET(STDIN_FILENO, &fd_set_read);
00242         FD_SET(PT_GET_MASTER_FD(p), &fd_set_read);
00243         ec_neg1( select(FD_SETSIZE, &fd_set_read, NULL, NULL, NULL) )
00244         if (FD_ISSET(STDIN_FILENO, &fd_set_read)) {
00245             ec_neg1( nread = read(STDIN_FILENO, &buf, sizeof(buf)) )
00246             ec_neg1( writeall(PT_GET_MASTER_FD(p), buf, nread) )
00247         }
00248         if (FD_ISSET(PT_GET_MASTER_FD(p), &fd_set_read)) {
00249             if ((nread = read(PT_GET_MASTER_FD(p), &buf,
00250               sizeof(buf))) > 0) {
00251                 ec_false( ev_write(buf, nread) )
00252                 ec_neg1( writeall(STDOUT_FILENO, buf, nread) )
00253             }
00254             else if (nread == 0 || (nread == -1 && errno == EIO))
00255                 break;
00256             else
00257                 EC_FAIL
00258         }
00259     }
00260     ec_neg1( ev_write(NULL, 0) )
00261     fprintf(stderr,
00262       "EOF or error reading stdin or master pseudo-terminal; exiting\n");
00263     ok = true;
00264     EC_CLEANUP
00265 /*[main-4]*/
00266 EC_CLEANUP_BGN
00267     if (p != NULL)
00268         (void)pt_close_master(p);
00269     tc_restore();
00270     ev_close();
00271     printf("\n");
00272     exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
00273 EC_CLEANUP_END
00274 }
00275 /*[]*/

Generated on Fri Apr 23 10:56:56 2004 for AUP2 Example Source by doxygen 1.3.1