Added GPL blurbs to err_mac.h and stripchart.py.
[stripchart.git] / strip.c
1 /*
2   strip - program for stripcharting ASCII data read from a named pipe.
3   Copyright (C) 2008, William Trevor King
4
5   This program is free software; you can redistribute it and/or
6   modify it under the terms of the GNU General Public License as
7   published by the Free Software Foundation; either version 3 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
13   See the GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18   02111-1307, USA.
19
20   The author may be contacted at <wking@drexel.edu> on the Internet, or
21   write to Trevor King, Drexel University, Physics Dept., 3141 Chestnut St.,
22   Philadelphia PA 19104, USA.
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <sys/stat.h> /* mkfifo(), S_IRUSR, S_IWUSR */
31 #include "err_mac.h"
32 #include "stripchart.h"
33
34 #define BUFSZ 81
35
36 //#define AUTOGEN /* for testing */
37
38
39 static int string2double(char *string, double *pDouble);
40 static int getargs(int argc, char **argv, unsigned int *pFlags,
41                    char **pTitle, char **pGeometry, char **pPipe_name,
42                    double *pY_max, double *pY_min, int *pPoints_shown_at_once);
43 static int open_pipe(char *name, unsigned int flags, FILE **pInStream);
44 static int close_pipe(char *name, FILE **pInStream);
45 static int get_num(FILE *stream, double *px);
46
47 static const char options[] = "t:g:p:cuM:m:n:avh";
48 static const char *func_name = NULL;
49 static const char *description = 
50  "Pops up an X window strip chart, and a FIFO to dump data into";
51
52 static int verbose = 0;
53
54 #define FLAG_CREATE_FIFO 0x1
55 #define FLAG_AXES_ON 0x2
56
57 static void help(char *title, char *geometry, char *pipe_name,
58                  double y_max, double y_min, int num_points)
59 {
60   printf("usage: %s [-%s]\n", func_name, options);
61   puts(description);
62   printf("Version: %s\n\n", VERSION);
63
64   puts("Options:");
65
66   printf("-t <title>\tSet the window title (default: %s)\n", title);
67   printf("-g <geometry>\tSet the initial window geometry (default: %s)\n", geometry);
68   printf("-p <file>\tSet FIFO name (default: %s)\n", pipe_name);
69
70   printf(" %s can either create its own FIFO or use an already existing one.\n",
71          func_name);
72   puts(" Using an existing FIFO avoids the following race condition:");
73   puts("\tFork");
74   printf("\tProcess 1 starts %s\n", func_name);
75   puts("\tProcess 2 sends data to FIFO, but it doesn't exist,");
76   puts("\t\tso file system creates a new file");
77   puts("\tProcess 1 gets around to trying to create the FIFO,");
78   puts("\t\tbut fails because the file already exists");
79   printf("-u\t%s uses an already created FIFO\n", func_name);
80   printf("-c\t%s create the input FIFO (default)\n", func_name);
81
82   printf("-M\tSet Maximum y value (default: %g)\n", y_max);
83   printf("-m\tSet minimum y value (default: %g)\n", y_min);
84   printf("-n\tSet number of points shown at once (default: %d)\n", num_points);
85   puts("-a\tToggle axes (default: on)");
86
87   puts("-v\tIncrement verbosity");
88   puts("-h\tDisplay this help and exit");
89   exit(1);
90 }
91
92 int main(int argc, char **argv)
93 {
94   stripchart_t *chart = NULL;
95   FILE *input;
96   double x=0, y_max, y_min;
97   unsigned int flags;
98   int points_shown_at_once;
99   char *title, *geometry, *pipe_name;
100
101   CHK( getargs(argc, argv, &flags,
102                &title, &geometry, &pipe_name,
103                &y_max, &y_min, &points_shown_at_once) );
104 #ifndef AUTOGEN
105   CHK( open_pipe(pipe_name, flags, &input) );
106   if (verbose)
107     fprintf(stderr, "creating stripchart on pipe %s with geom %s\n",
108             pipe_name, geometry);
109 #endif
110   CHK( stripchart_create(&chart, title, geometry,
111                          y_min, y_max, 1.0, 
112                          points_shown_at_once,
113                          flags & FLAG_AXES_ON) );
114   do {
115 #ifndef AUTOGEN
116     if (get_num(input, &x) != 0)
117       break;
118 #else 
119     x += 0.5;
120     if (x == 9) break;
121     usleep(100000);
122 #endif
123     CHK( stripchart_point(chart, x) );
124   } while (TRUE);
125   CHK( stripchart_destroy(&chart) );
126 #ifndef AUTOGEN
127   CHK( close_pipe(pipe_name, &input) );
128 #endif
129   return 0;
130 }
131
132 static int string2double(char *string, double *pDouble)
133 {
134   char *endp;
135   if (errno != 0) {
136     //perror("sloppy error checking");
137     //fprintf(stderr, "errno = %d\n", errno);
138     //return 1; /* unrecognized input */
139     /* HACK!!!  Some Xlib function calls (XOpenDisplay, and others) seem to set
140      * errno to 29, or 11, but still return successfully.  I have no idea why,
141      * since I can't find any mention of errno in the Xlib documents, but we'll
142      * just ignore any previous errnos and hope everything works out...
143      */
144     errno = 0;
145   }
146   *pDouble = strtod(string, &endp);
147   if (errno != 0 || endp[0] != '\0') {
148     if (errno != 0) {
149       perror("strtod");
150       fprintf(stderr, "could not parse '%s'\n", string);
151       fprintf(stderr, "endp = \"%s\"\n", endp);
152       fprintf(stderr, "errno = %d\n", errno);
153     }
154     return 1; /* unrecognized input */
155   }
156   return 0;
157 }
158
159 static int getargs(int argc, char **argv, unsigned int *pFlags,
160                    char **pTitle, char **pGeometry, char **pPipe_name,
161                    double *pY_max, double *pY_min, int *pPoints_shown_at_once)
162 {
163   int c;
164   double temp;
165
166   func_name = argv[0];
167   *pFlags = FLAG_CREATE_FIFO | FLAG_AXES_ON;
168   *pTitle = "Stripchart";
169   *pGeometry = "500x200+0+0";
170   *pPipe_name = "strip_pipe";
171   *pY_max = 10.0;
172   *pY_min = -10.0;
173   *pPoints_shown_at_once = 100;
174
175   while ((c=getopt(argc, argv, options)) != -1) {
176     switch (c) {
177     case 't':
178       *pTitle = optarg;
179       break;
180     case 'g':
181       *pGeometry = optarg;
182       break;
183     case 'p':
184       *pPipe_name = optarg;
185       break;
186     case 'u':
187       *pFlags &= ~FLAG_CREATE_FIFO;
188       break;
189     case 'c':
190       *pFlags |= FLAG_CREATE_FIFO;
191       break;
192     case 'M':
193       if (string2double(optarg, pY_max) != 0)
194         goto error;
195       break;
196     case 'm':
197       if (string2double(optarg, pY_min) != 0)
198         goto error;
199       break;
200     case 'n':
201       if (string2double(optarg, &temp) != 0)
202         goto error;
203       *pPoints_shown_at_once = (int)temp;
204       break;
205     case 'a':
206       *pFlags ^= FLAG_AXES_ON;
207       break;
208     case 'v':
209       verbose++;
210       break;
211     default:
212       printf("Unrecognized option '%c'\n", c);
213     case 'h': /* default falls through */
214     error:
215       printf("Problem with option '%c' (%s)\n", c, optarg);
216       help(*pTitle, *pGeometry, *pPipe_name, *pY_max, *pY_min, *pPoints_shown_at_once);
217       break;
218     }
219   }
220   return 0;
221 }
222
223 static int open_pipe(char *name, unsigned int flags, FILE **pStream)
224 {
225   if (flags & FLAG_CREATE_FIFO) {
226     /* make a fifo with read/write permision for the user */
227     if (mkfifo(name, S_IRUSR | S_IWUSR) != 0) {
228       perror("mkfifo");
229       fprintf(stderr, "Error creating FIFO '%s', aborting\n", name);
230       AST(1==0);
231     }
232   }
233   AST( (*pStream = fopen(name, "a+")) != NULL );
234   return 0;
235 }
236
237 static int close_pipe(char *name, FILE **pInStream)
238 {
239   CHK( fclose(*pInStream) );
240   CHK( remove( name ) );
241   return 0;
242 }
243
244 static int get_num(FILE *stream, double *px)
245 {
246   char buf[BUFSZ];
247   
248   if (fgets(buf, BUFSZ, stream) == NULL)
249     return 1; /* end of file or error */
250   if (buf[strlen(buf)-1] == '\n')
251     buf[strlen(buf)-1] = '\0';
252   if ( string2double(buf, px) != 0)
253     return 1;
254   return 0;
255 }