Update Makefile to handle all project files in uncompressed state
[atscap.git] / common.h
bloba86dcd1ff450d11ff842f3ca38d17b30375bc5a4
1 /*****************************************************************************
2 * common.h (c) Copyright 2007 by inkling@users.sourceforge.net
4 * This software is part of the atscap 1.1 distribution codebase.
6 * atscap is free software; you may only redistribute it and/or modify
7 * it under the terms of the GNU General Public License, Version 2 or later,
8 * as published by the Free Software Foundation.
10 * atscap is distributed to you AS-IS, in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License Version 2
16 * along with this program; if not, write the Free Software Foundation, Inc.,
17 * at 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <stdarg.h>
23 #ifndef COMMON_H
24 #define COMMON_H
26 #ifndef WHO
27 #define WHO (char *) __FUNCTION__
28 #define WHERE (int) __LINE__
29 #endif
31 /******************************** alloc functions ***************************/
32 #define ALLOC_MAX 32
33 #define ALLOC_NAME_MAX 40
34 typedef struct {
35 char * name;
36 void * addr;
37 int size;
38 } alloc_t;
40 alloc_t allocs[ ALLOC_MAX ];
41 int alloc_idx = 0;
44 //static
45 void
46 init_allocs ( void )
48 int i;
50 memset( allocs, 0, sizeof(allocs));
52 for (i = 0; i < ALLOC_MAX; i++) {
53 allocs[i].addr = NULL;
54 allocs[i].name = NULL;
59 //static
60 void
61 show_allocs( void )
63 int i, t;
64 alloc_t *m;
66 // WHOAMI;
68 m = allocs;
70 t = 0;
71 fprintf(stdout, "\nPointers Size Name\n");
73 for (i = 0; i < ALLOC_MAX; i++) {
74 if (NULL != m[i].addr) {
75 fprintf( stdout, "0x%08X %8d %s\n",
77 /* broken for 64 bits? use off_t instead? */
78 (unsigned int)m[i].addr,
80 m[i].size,
81 m[i].name );
83 t += m[i].size;
87 fprintf(stdout, "Pointers %9d total bytes\n\n", t);
91 //static
92 void *
93 ifree ( void * p, char *n )
95 int i;
96 alloc_t *m;
98 // WHOAMI;
100 m = allocs;
102 if (NULL == p) return NULL;
104 for (i = 0; i < ALLOC_MAX; i++)
105 if (p == m[i].addr) break;
107 if (i == ALLOC_MAX) {
108 #ifdef ATSCAP
109 dvrlog( LOG_INFO,
110 "ifree %p not found for %s", p, n);
111 #else
112 fprintf( stdout,
113 "ifree %p not found for %s\n", p, n);
114 #endif
115 return NULL;
117 m = &allocs[i];
119 // fprintf( stdout, "ifreed %s %d\n", m->name, m->size);
121 free(m->addr);
122 m->addr = NULL;
123 m->size = 0;
124 free(m->name);
125 m->name = NULL;
126 alloc_idx--;
128 return NULL;
132 //static
133 void *
134 imalloc ( size_t z, char *n )
136 int i;
137 void *p;
138 alloc_t *m;
140 m = allocs;
142 for (i = 0; i < ALLOC_MAX; i++)
143 if (NULL == m[i].addr ) break;
145 if (i == ALLOC_MAX) {
146 #ifdef ATSCAP
147 dvrlog( LOG_INFO,
148 "ialloc full %s %d, increase ALLOC_MAX",
149 n, z);
151 #else
152 fprintf( stdout,
153 "ialloc full %s %d, increase ALLOC_MAX\n",
154 n, z );
155 #endif
156 return NULL;
159 p = malloc( z );
160 if (NULL == p) return NULL;
161 m->addr = p;
162 m->size = z;
163 m->name = calloc( 1, ALLOC_NAME_MAX );
164 strncpy( m->name, n, ALLOC_NAME_MAX );
165 m->name[ ALLOC_NAME_MAX - 1 ] = 0;
166 alloc_idx++;
168 return p;
172 //static
173 void *
174 icalloc( int x, int y, char *n )
176 void * p;
177 int i;
178 alloc_t *m;
180 // WHOAMI;
181 // if (0 != arg_fmsg)
182 // fprintf( stdout, "%s %d %s\n", WHO, z, n );
184 m = allocs;
186 for (i = 0; i < ALLOC_MAX; i++)
187 if (NULL == m[i].addr ) break;
189 if (i == ALLOC_MAX) {
190 #ifdef ATSCAP
191 dvrlog( LOG_INFO,
192 "icalloc full %s %dx%d, increase ALLOC_MAX",
193 n, x, y );
195 #else
196 fprintf( stdout,
197 "icalloc full %s %dx%d, increase ALLOC_MAX\n",
198 n, x, y );
199 #endif
200 return NULL;
203 p = calloc( x, y );
204 if (NULL == p) return NULL;
206 m = &allocs[i];
207 m->addr = p;
208 m->size = x * y;
210 m->name = calloc( 1, ALLOC_NAME_MAX );
211 strncpy( m->name, n, ALLOC_NAME_MAX );
212 m->name[ ALLOC_NAME_MAX - 1 ] = 0;
214 alloc_idx++;
216 return p;
219 // incremental realloc, does not shrink, only grows by n bytes
220 //static
221 void *
222 irealloc ( void * p, int z, char *n )
224 int i;
225 alloc_t *m;
227 // WHOAMI;
228 // fprintf( stdout, "%s p %p z %d\n", WHO, p, z );
230 m = allocs;
233 if (NULL == p) return NULL;
234 if (0 == z) return p;
236 for (i = 0; i < ALLOC_MAX; i++)
237 if (p == m[i].addr) break;
239 if (i == ALLOC_MAX) {
240 #ifdef ATSCAP
241 dvrlog( LOG_INFO,
242 "irealloc %p not found for %s %d", p, n, z);
243 #else
244 fprintf( stdout,
245 "irealloc %p not found for %s %d\n", p, n, z);
246 #endif
247 return NULL;
250 m = &allocs[i];
252 strncpy( m->name, n, ALLOC_NAME_MAX );
253 m->name[ ALLOC_NAME_MAX - 1] = 0;
255 m->size += z;
256 m->addr = realloc( m->addr, m->size );
258 return m->addr;
260 /*************************** end of alloc functions *************************/
263 /********************************* text functions ***************************/
264 #define F_PATH 1
265 #define F_PFILE 2
266 #define F_TFILE 3
267 #define F_FILE 4
269 //static
270 void
271 filebase ( char *d, char *s, int o )
273 char *t;
275 *d = 0;
277 /* limit option flags to non-bogus */
278 if (0 == o) return;
279 if (0 > o) return;
281 /* limit string handling to non-bogus */
282 if (NULL == d) return;
283 if (NULL == s) return;
284 if (0 == *s) return;
286 /* mutually exclusive options for how to truncate the source name */
287 switch( o )
289 /* path only, with / */
290 case F_PATH:
291 strcpy( d, "./" );
292 t = strrchr( s, '/' );
293 if (NULL != t) {
294 strcpy( d, s );
295 t = strrchr( d, '/');
296 t++;
297 *t = 0;
299 break;
301 /* path and file, without last .ext */
302 case F_PFILE:
303 strcpy( d, "./" );
304 t = strrchr( s, '/' );
305 if (NULL == t) d += 2;
306 strcpy( d, s );
307 t = strrchr( d, '.');
308 if (NULL != t) *t = 0; /* truncate at last . */
309 break;
311 /* truncated file only, without last .ext */
312 case F_TFILE:
313 t = strrchr( s, '/' ); /* skip path */
314 if (NULL != t) {
315 t++;
316 strcpy( d, t );
318 /* truncate at last . */
319 t = strrchr( d, '.');
320 if (NULL != t) *t = 0;
321 } else {
322 /* no path, truncate at last . */
323 strcpy( d, s );
324 t = strrchr( d, '.');
325 if (NULL != t) *t = 0;
327 break;
329 /* file only with .ext */
330 case F_FILE:
331 t = strrchr( s, '/' );
332 if (NULL != t) {
333 t++;
334 strcpy( d, t );
335 } else {
336 strcpy( d, s );
338 break;
340 default:
341 break;
346 /* long long to ascii comma delimited to make big numbers easier to read.
347 Assumes dest has enough bytes for result (19 digits + 6 commas + nul).
348 Largest number is around +/- 9,000,000,000,000,000,000 (US quintillion).
350 PROOF:
352 value sl dest dl dl computation
354 1 1 1 1 (0 * 4) + 1 = 1
355 10 2 10 2 (0 * 4) + 2 = 2
356 100 3 100 3 (1 * 4) - 1 = 3
357 1000 4 1,000 5 (1 * 4) + 1 = 5
358 10000 5 10,000 6 (1 * 4) + 2 = 6
359 100000 6 100,000 7 (2 * 4) - 1 = 7
360 1000000 7 1,000,000 9 (2 * 4) + 1 = 9
361 10000000 8 10,000,000 10 (2 * 4) + 2 = 10
362 100000000 9 100,000,000 11 (3 * 4) - 1 = 11
363 1000000000 10 1,000,000,000 13 (3 * 4) + 1 = 13
365 NOTE: atoll() breaks after 2^30 on 32-bit with some older compilers.
366 Auto-verification using atoll on char c[] doesn't always work.
367 This function also depends on snprintf getting it right.
369 10000000000 11 10,000,000,000 14 (3 * 4) + 2 = 14
371 #define COMMA_MAX 32
373 //static
374 void
375 lltoasc ( char *dest, long long value )
377 char *s, *b, *d; /* source and bottom boundary for d */
378 int i, k, sl, dl; /* loops, source len, destination len */
379 char c[COMMA_MAX]; /* value in ascii */
381 d = dest;
383 /* Don't want the sign confusing the comma calculation so fix it here. */
384 if ( value < 0LL ) {
385 // value ^= -1LL; /* one */
386 // value++; /* way */
387 value = 0LL - value; /* or another */
388 /* if still negative, it is largest negative and will not complement. */
389 if (value >= 0LL) *d++ = '-';
392 snprintf( c, COMMA_MAX, "%lld", value );
394 if (value > 999LL) { /* commas needed ? */
395 b = d; /* save stop pointer */
396 sl = strlen( c ); /* src len */
397 dl = ((sl / 3) * 4) + (sl % 3); /* dest len */
398 if ( 0 == ( sl % 3)) dl--; /* fixup for last , */
400 d[dl] = 0; /* NUL term */
401 dl--; /* and back up */
403 if ( dl < COMMA_MAX ) { /* will added commas fit? */
405 /* copy bytes from s to d in reverse order, adding , to d every 3 bytes */
407 k = sl - 1; /* loop limit */
408 s = c + k; /* point: 1's place end of c */
409 d += dl; /* point: computed end of d */
411 for ( i = sl; i > 0; ) {
412 if ( (d < b) || (s < c) ) break; /* boundary */
413 i--;
414 *d-- = *s--; /* copy & decrement */
415 if ( (d < b) || (s < c) ) break; /* boundary */
417 if (0 == ((sl-i) % 3)) *d-- = ',';
418 if (d < b) break; /* boundary check */
420 } else {
421 strncpy( d, c, COMMA_MAX ); /* commas won't fit */
423 } else {
424 strncpy( d, c, COMMA_MAX ); /* no commas needed */
426 return;
430 /* build a list of pointers up to n pointers from string s using delim c */
431 /* s is modified with NUL terms replacing each delim c */
432 //static
433 void
434 build_args( char **p, int n, char *s, char c, char *caller )
436 int i;
437 char *r, *t;
439 for (i = 0; i < n; i++) p[i] = NULL;
441 r = s;
443 for (i = 0; i < n; i++) {
444 if (0 == *r) break;
445 p[i] = r;
446 t = strchr( r, c );
447 if (NULL == t) break;
448 *t = 0;
449 t++;
450 r = t;
455 /* Similar to but different from strncpy:
456 No string greater than 16k allowed to be copied, log > 16k and NULLs.
457 Does not pad destination with 0's if length of source is shorter than n.
458 There is no valid reason to spend cpu cycles filling memory with zeros.
459 This will always null terminate at n-1 even if s is n len bytes long.
460 d result will always be n-1 characters long, at most.
462 /* static */
463 void
464 astrncpy ( char *d, char *s, unsigned int n )
466 int c = n;
467 char *t = d;
468 /* log nulls */
469 if ( (NULL == d) || (NULL == s) ) {
470 #ifdef ATSCAP
471 dvrlog( LOG_INFO, "astrncpy() %p %p NULL", d, s);
472 #else
473 fprintf( stdout, "\nastrncpy() %p %p NULL\n", d, s);
474 #endif
475 return;
477 /* log 0 and > 65535 len */
478 if ( (0 == n) || (n > 16383) ) {
479 #ifdef ATSCAP
480 dvrlog( LOG_INFO, "astrncpy() %d chars", n);
481 #else
482 fprintf( stdout, "\nastrncpy() %d chars\n", n);
483 #endif
484 return;
487 /* count auto decrements, pointers auto increment */
488 while (c-- > 0) if (0 == (*t++ = *s++)) break;
490 /* always force last byte 0 */
491 d[n-1] = 0;
495 /* maximum amount to text to append with each call */
496 #define PRINTF_MAX 16384
497 /* Incremental snprintf, appends to string 'a' until size 'b' reached.
498 This keeps 'b' limited to 'a' string alloc instead of blind melon limit.
499 First call should have a strlen 0, *a = 0 or you get variable junk.
501 //static
502 void
503 asnprintf ( char *a, size_t b, const char *fmt, ... )
505 char *p, t[PRINTF_MAX];
506 int la;
507 int lt;
508 va_list ap;
510 if (NULL == a) return;
511 if (b < 1) return;
513 memset(t, 0, sizeof(t));
514 va_start(ap, fmt);
515 /* variable arg macro start, pass fmt and ap, variable arg macro end */
516 vsnprintf( t, sizeof(t)-1, fmt, ap );
517 va_end(ap);
519 la = strlen(a);
520 lt = strlen(t);
522 /* bit bucket does not overflow? */
523 if ( (la + lt + 1) < b ) {
524 p = &a[la];
525 /* nul term it */
526 astrncpy( p, t, lt + 1 );
528 /* if bit bucket does overflow, silently fail and don't exceed boundary */
530 /**************************** end of text functions *************************/
533 /******************************* librt functions ****************************/
534 #ifdef USE_LIBRT
535 #define RTC_MAX 3
536 char *clock_text[ RTC_MAX ] = {
537 "REALTIME",
538 "PROCESS_CPUTIME_ID",
539 "THREAD_CPUTIME_ID"
542 clockid_t clock_ids[ RTC_MAX ] = {
543 CLOCK_REALTIME,
544 CLOCK_PROCESS_CPUTIME_ID,
545 CLOCK_THREAD_CPUTIME_ID
547 clockid_t clock_method = CLOCK_PROCESS_CPUTIME_ID;
548 long long clock_res; /* (tv.sec<<32) | tv.nsec */
549 int clock_idx;
551 /* find the most accurate clock and set clock_method global */
552 //static
553 void
554 test_clock_res ( void )
556 struct timespec res;
557 int i, j;
558 long long r, s;
560 j = -1;
561 r = 1000000000LL; /* worst case scenario is 1 clock per second */
563 for (i = 0; i < RTC_MAX; i++) {
565 /* is clock res return valid? */
566 if (0 == clock_getres( clock_ids[i], &res)) {
567 s = res.tv_sec;
568 s <<= 32;
569 s |= res.tv_nsec;
571 /* is clock res non-zero? */
572 if (0LL != s)
573 if (s < r) {
574 j = i;
575 r = s;
580 if (-1 == j) {
581 fprintf(stderr, "%s has no clock method? check librt\n", NAME);
582 exit(1);
585 /* use last lowest value in l for the syslog */
586 clock_method = clock_ids[j];
587 clock_idx = j;
588 clock_res = r;
589 fprintf(stdout, "CLOCK_METHOD is %d %s\n",
590 clock_idx, clock_text[clock_idx]);
592 /* USE_LIBRT */
593 #endif
594 /*************************** end of librt functions *************************/
597 /******************************* file functions *****************************/
598 #ifdef ATSCAP
599 /* Copy source file s to destination file d, a is 0 copy or 1 append:
600 If source contains '*', source is used for glob list.
601 If destination term with '/', [glob] source appends to dest.
602 NOTE: It could *always* glob, however glob() uses a lot of cycles.
603 Also, it tries to behave like cp -a and keep file date and mode bits.
604 TODO? alloc buf fs.blocksize or page-size? 4k is OK
607 //static
609 filecopy ( char *d, char *s, int a )
611 char buf[4096], ni[512], no[512], fb[256], *p;
612 int i, j, k, o, r, w, z, e, g, f, rc;
613 struct utimbuf ut;
614 int upm;
615 glob_t globt;
616 struct stat64 fs;
618 z = sizeof(buf);
620 /* sanity checks */
621 if (NULL == d) return -1;
622 if (NULL == s) return -1;
623 if (0 == *d) return -1;
624 if (0 == *s) return -1;
626 e = 0;
627 f = 1;
628 g = 0;
630 /* glob needed for * in filename? */
631 p = strchr( s, '*' );
632 if (NULL == p) p = strchr( s, '?' );
633 if (NULL != p) g = ~0;
635 if (0 != g) {
636 /* WARNING: no returns until globt is freed, only continues and breaks */
637 glob( s, 0, NULL, &globt );
639 f = globt.gl_pathc;
640 advrlog( LOG_INFO, "%s glob %s files %d", WHO, s, f);
643 /* any more glob files left to do, or is single file? */
644 for (j = 0; j < f; j++) {
646 /* set name in and name out */
647 astrncpy( ni, s, sizeof(ni) );
648 astrncpy( no, d, sizeof(no) );
650 /* glob will change source name to glob index if found */
651 if (0 != g) astrncpy( ni, globt.gl_pathv[ j ], sizeof(ni) );
653 /* remove any trailing / */
654 p = no + strlen( no );
655 p--;
656 if ('/' == *p) *p = 0;
658 /* destination exists? */
659 rc = stat64( no, &fs );
660 if (0 == rc) {
661 if (0 != (S_IFDIR & fs.st_mode)) {
662 filebase( fb, ni, F_FILE );
663 asnprintf( no, sizeof(no), "/%s", fb );
667 /* keep source metadata: cp -a emulation sets mtime and mode after copy */
668 rc = stat64( ni, &fs);
670 /* skip bad file for whatever reason */
671 if (rc < 0) {
672 dvrlog( LOG_INFO, "%s can't stat %s", WHO, ni);
673 continue;
676 /* clear other time stamps */
677 memset( &ut, 0, sizeof(ut) );
678 ut.modtime = fs.st_mtime;
679 ut.actime = fs.st_atime;
681 upm = fs.st_mode;
683 advrlog( LOG_INFO, "%s %s %s", (0 == a)?"copy":"append", ni, no );
685 /* peter knaggs says read only helps luser mode */
686 i = open( ni, FILE_ROMODE );
687 if (i < 3) {
688 dvrlog( LOG_INFO, "%s can't open %s err %d", WHO, ni, errno);
689 e = -1;
690 continue;
693 /* copy, mode is octal */
694 if (0 == a)
695 o = open( no, FILE_WMODE, 0644 );
697 /* append. seeks end of file instead of O_APPEND flag with NFS problems */
698 if (0 != a)
699 o = open( no, O_RDWR | O_CREAT, 0644 );
701 if (o < 3) {
702 dvrlog( LOG_INFO, "%s can't open %s", WHO, no);
703 close ( i );
704 e = -1;
705 continue;
708 /* append sets file position to end, should avoid NFS problems?
709 no. append is broken. don't use it here.
711 if (0 != a) lseek( o, 0, SEEK_END );
713 r = 1;
714 e = 0;
715 k = 0;
716 while (r > 0) {
717 r = read( i, buf, z );
718 if (r > 0) {
719 w = write( o, buf, r );
720 k += r;
721 if (w < 1) {
722 dvrlog( LOG_INFO, "%s can't write %s", WHO, no);
723 e = -1;
724 break;
727 if (w != r) {
728 dvrlog( LOG_INFO, "%s wrote %d of %d to %s",
729 WHO, w, r, no);
730 e = -1;
731 break;
734 if (r != z) {
735 advrlog( LOG_INFO, "%s EOF %s", WHO, ni);
736 break;
740 if (r < 0) {
741 dvrlog( LOG_INFO, "%s can't read %s", WHO, no);
742 e = -1;
743 break;
746 close( i );
747 fsync( o );
748 close( o );
750 /* Change file permissions after close to match original mode then set
751 mtime after chmod. This prevents guide from reloading every time it
752 restarts with recent EPGs, and keeps auto-EPG reload on 3hr meridian.
754 NOTE: It is not expected that you will have to restart it very often.
755 This is mostly to make it easier if you're trying to add new features.
756 The wait for guide load can be very annoying after a few dozen times. :>
758 chmod( no, upm );
759 utime( no, &ut );
762 /* don't forget to free what glob allocated */
763 if (0 != g) globfree( &globt );
765 return e;
768 /* ATSCAP */
769 #endif
770 /**************************** end of file functions *************************/
773 /* COMMON_H */
774 #endif