~ubuntu-branches/ubuntu/lucid/fotoxx/lucid

« back to all changes in this revision

Viewing changes to zfuncs.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Torres Batan
  • Date: 2009-08-20 12:27:47 UTC
  • Revision ID: james.westby@ubuntu.com-20090820122747-jr7txlqf1a58omzk
Tags: upstream-8.3
ImportĀ upstreamĀ versionĀ 8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**************************************************************************
 
2
   zfuncs   collection of Linux and GDK/GTK utility functions
 
3
 
 
4
   Copyright 2006, 2007, 2008, 2009  Michael Cornelison
 
5
   source URL:  kornelix.squarespace.com
 
6
   contact: kornelix@yahoo.de
 
7
   
 
8
   This program is free software: you can redistribute it and/or modify
 
9
   it under the terms of the GNU General Public License as published by
 
10
   the Free Software Foundation, either version 3 of the License, or
 
11
   (at your option) any later version.
 
12
 
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
   GNU General Public License for more details.
 
17
 
 
18
   You should have received a copy of the GNU General Public License
 
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
***************************************************************************/
 
22
 
 
23
//     zfuncs version  v.2.23   2009.08.31
 
24
 
 
25
#include "zfuncs.h"
 
26
 
 
27
/**************************************************************************
 
28
   system-level utility functions
 
29
***************************************************************************/
 
30
 
 
31
//  crash with error message and traceback dump to stdout
 
32
//  works like printf
 
33
 
 
34
void appcrash(cchar *pMess, ... )
 
35
{
 
36
   va_list  arglist;
 
37
   char     message[200];
 
38
   void     *stacklist[50];
 
39
   int      ii, nstack = 50;
 
40
   char     **stackents;
 
41
 
 
42
   va_start(arglist,pMess);
 
43
   vsnprintf(message,200,pMess,arglist);
 
44
   va_end(arglist);
 
45
   
 
46
   printf("appcrash: \n %s \n",message);
 
47
   
 
48
   nstack = backtrace(stacklist,nstack);                                   //  good for g++ -rdynamic
 
49
   stackents = backtrace_symbols(stacklist,nstack);
 
50
   for (ii = 0; ii < nstack; ii++) 
 
51
      printf(" %s \n",stackents[ii]);
 
52
 
 
53
   abort();                                                                //  good for gdb backtrace
 
54
}
 
55
 
 
56
 
 
57
//  crash with error message and traceback dump in popup window
 
58
//  works like printf
 
59
 
 
60
void zappcrash(cchar *pMess, ... )                                         //  v.2.22
 
61
{
 
62
   va_list     arglist;
 
63
   FILE        *fid;
 
64
   int         ii, err, nstack = 50;
 
65
   char        message[300];
 
66
   void        *stacklist[50];
 
67
   char        **stackents;
 
68
 
 
69
   va_start(arglist,pMess);
 
70
   vsnprintf(message,299,pMess,arglist);
 
71
   va_end(arglist);
 
72
   
 
73
   fid = fopen("zappcrash","w");
 
74
 
 
75
   fprintf(fid,"zappcrash: \n %s \n",message);
 
76
 
 
77
   nstack = backtrace(stacklist,nstack);
 
78
   stackents = backtrace_symbols(stacklist,nstack);
 
79
   for (ii = 0; ii < nstack; ii++) 
 
80
      fprintf(fid," %s \n",stackents[ii]);
 
81
 
 
82
   fclose(fid);
 
83
   
 
84
   err = system("xdg-open zappcrash");
 
85
   abort();
 
86
}
 
87
 
 
88
 
 
89
//  Output a message to stdout and wait for user ACK.
 
90
//  Works like printf.
 
91
 
 
92
void apppause(cchar *pMess, ... )
 
93
{
 
94
   va_list  arglist;
 
95
   char     message[200];
 
96
 
 
97
   va_start(arglist,pMess);
 
98
   vsnprintf(message,200,pMess,arglist);
 
99
   va_end(arglist);
 
100
 
 
101
   printf("pause: %s \n",message);
 
102
   printf("*** press return to continue: ");
 
103
   getchar();
 
104
   return;
 
105
}
 
106
 
 
107
void apppause()
 
108
{
 
109
   printf("*** pause, press return to continue: ");
 
110
   getchar();
 
111
   return;
 
112
}
 
113
 
 
114
 
 
115
//  catch segfaults and produce backtrace dumps on-screen
 
116
 
 
117
void sighandler(int signal)
 
118
{
 
119
   zappcrash("segment fault");
 
120
   return;
 
121
}
 
122
 
 
123
 
 
124
//  application initialization function to catch segfaults
 
125
 
 
126
void catch_signals()                                                       //  v.2.22
 
127
{
 
128
   struct sigaction  sigact;
 
129
 
 
130
   sigact.sa_handler = sighandler;
 
131
   sigemptyset(&sigact.sa_mask);
 
132
   sigact.sa_flags = 0;
 
133
   sigaction(SIGSEGV,&sigact,0);
 
134
   return;
 
135
}
 
136
 
 
137
 
 
138
//  get time in real seconds (since 2000.01.01 00:00:00)                   //  v.2.12
 
139
 
 
140
double get_seconds()
 
141
{
 
142
   timeval  time1;
 
143
 
 
144
   gettimeofday(&time1,0);
 
145
   return  time1.tv_sec + 0.000001 * time1.tv_usec - 946684800.0;
 
146
}   
 
147
 
 
148
 
 
149
//  start a timer or get elapsed time with millisecond resolution.
 
150
 
 
151
void start_timer(double &time0)                                            //  double  v.2.20
 
152
{
 
153
   timeval  timev;
 
154
 
 
155
   gettimeofday(&timev,0);
 
156
   time0 = timev.tv_sec + 0.000001 * timev.tv_usec;
 
157
   return;
 
158
}
 
159
 
 
160
double get_timer(double &time0)
 
161
{
 
162
   timeval  timev;
 
163
   double   time;
 
164
 
 
165
   gettimeofday(&timev,0);
 
166
   time = timev.tv_sec + 0.000001 * timev.tv_usec;
 
167
   return time - time0;
 
168
}
 
169
 
 
170
 
 
171
//  start a process CPU timer or get elapsed process CPU time
 
172
//  returns seconds with millisecond resolution
 
173
 
 
174
void start_CPUtimer(double &time0)                                         //  double  v.2.20
 
175
{
 
176
   time0 = CPUtime();
 
177
   return;
 
178
}
 
179
 
 
180
double get_CPUtimer(double &time0)
 
181
{
 
182
   return CPUtime() - time0;
 
183
}
 
184
 
 
185
 
 
186
//  get elapsed CPU time used by current process
 
187
//  returns seconds with millisecond resolution
 
188
 
 
189
double CPUtime()
 
190
{
 
191
   clock_t ctime = clock();
 
192
   double dtime = ctime / 1000000.0;
 
193
   return dtime;
 
194
}
 
195
 
 
196
 
 
197
//  start a detached thread using a simplified protocol                    //  v.2.11
 
198
//  thread exit:  pthread_exit(0);
 
199
 
 
200
void start_detached_thread(void * threadfunc(void *), void * arg)
 
201
{
 
202
   pthread_t         ptid;
 
203
   pthread_attr_t    ptattr;
 
204
   int               pterr;
 
205
 
 
206
   pthread_attr_init(&ptattr);
 
207
   pthread_attr_setdetachstate(&ptattr,PTHREAD_CREATE_DETACHED);
 
208
   pterr = pthread_create(&ptid,&ptattr,threadfunc,arg);
 
209
   if (pterr) appcrash("start_detached_thread() failure");
 
210
   return;
 
211
}
 
212
 
 
213
 
 
214
//  Synchronize execution of multiple threads.
 
215
//  Simultaneously resume NT calling threads.
 
216
//  from main():        synch_threads(NT)    /* setup to synch NT threads */
 
217
//  from each thread:   synch_threads()      /* suspend, resume simultaneously */
 
218
 
 
219
void synch_threads(int NT)
 
220
{
 
221
   static pthread_barrier_t   barrier;
 
222
   static int                 bflag = 0;
 
223
 
 
224
   if (NT) {                                                               //  main(), initialize
 
225
      if (bflag) pthread_barrier_destroy(&barrier);
 
226
      pthread_barrier_init(&barrier,null,NT);
 
227
      bflag = 1;
 
228
      return;
 
229
   }
 
230
 
 
231
   pthread_barrier_wait(&barrier);                                         //  thread(), wait for NT threads
 
232
   return;                                                                 //  unblock
 
233
}
 
234
 
 
235
 
 
236
//  safely access parameters from multiple threads
 
237
//  limitation: one lock for any number of parameters
 
238
 
 
239
mutex    zget_lock = PTHREAD_MUTEX_INITIALIZER;
 
240
 
 
241
int zget_locked(int &param)                                                //  lock and return parameter
 
242
{
 
243
   mutex_lock(&zget_lock);
 
244
   return param;
 
245
}
 
246
 
 
247
void zput_locked(int &param, int value)                                    //  set and unlock parameter
 
248
{
 
249
   param = value;
 
250
   mutex_unlock(&zget_lock);
 
251
   return;
 
252
}
 
253
 
 
254
int zadd_locked(int &param, int incr)                                      //  lock, increment, unlock, return
 
255
{
 
256
   int      retval;
 
257
 
 
258
   mutex_lock(&zget_lock);
 
259
   retval = param + incr;
 
260
   param = retval;
 
261
   mutex_unlock(&zget_lock);
 
262
   return retval;
 
263
}
 
264
 
 
265
 
 
266
//  sleep for specified time in seconds (double)
 
267
 
 
268
void zsleep(double dsecs)
 
269
{
 
270
   unsigned    isecs, nsecs;
 
271
   timespec    tsecs;
 
272
 
 
273
   if (dsecs == 0.0) return;   
 
274
   isecs = unsigned(dsecs);
 
275
   nsecs = unsigned(1000000000.0 * (dsecs - isecs));
 
276
   tsecs.tv_sec = isecs;
 
277
   tsecs.tv_nsec = nsecs;
 
278
   nanosleep(&tsecs,null);
 
279
   return;
 
280
}
 
281
 
 
282
 
 
283
//  malloc() and free() wrappers with auto crash on failure and log option.
 
284
//  overflow sentinel is placed after end of allocated memory
 
285
//  zmalloc() and zfree() calls are logged if log flag is on
 
286
//  benchmark zmalloc() + zfree(): usually < 1 microsec. on 2 GHz CPU
 
287
 
 
288
unsigned zmalloc_tot = 0;
 
289
#define zmalloc_add 12
 
290
 
 
291
char * zmalloc(size_t bytes, int log)
 
292
{
 
293
   void        *maddr = malloc(bytes + zmalloc_add);
 
294
   if (! maddr) appcrash("Memory request for %zu bytes failed. \n"         //  v.2.22
 
295
                         "Application will now terminate.",bytes);
 
296
 
 
297
   unsigned    *pbytes = (unsigned *) maddr;                   //  0..3          caller bytes
 
298
   char        *pvalid = (char *) maddr + 4;                   //  4..7          validity key "zmal"
 
299
   char        *puser =  (char *) maddr + 8;                   //  8..B+7        user data, B chars.
 
300
   char        *psent =  (char *) puser + bytes;               //  B+8..B+11     overflow sentinel "sent"
 
301
 
 
302
   *pbytes = bytes;
 
303
   strncpy(pvalid,"zmal",4);
 
304
   memset(puser,0,bytes);
 
305
   strncpy(psent,"sent",4);
 
306
   zmalloc_tot += bytes;
 
307
   if (log) printf("zmalloc loc: %p  bytes: %zu  total: %u \n",puser,bytes,zmalloc_tot);
 
308
   return puser;
 
309
}
 
310
 
 
311
 
 
312
//  free memory allocated by zmalloc(). checks for overflow.
 
313
 
 
314
void zfree(void *puser, int log)
 
315
{
 
316
   void        *maddr = (char *) puser - 8;
 
317
   unsigned    *pbytes = (unsigned *) maddr;
 
318
   char        *pvalid = (char *) maddr + 4;
 
319
   size_t      bytes = *pbytes;
 
320
   char        *psent = (char *) puser + bytes;
 
321
 
 
322
   if (! puser) appcrash("zfree: null address \n");
 
323
   if (strncmp("zmal",pvalid,4)) appcrash("zfree: invalid address %p \n",puser);
 
324
   if (strncmp("sent",psent,4)) appcrash("zfree: buffer overflow \n");
 
325
   *pvalid = *psent = 0;
 
326
   zmalloc_tot -= bytes;
 
327
   free(maddr);
 
328
   if (log) printf("zfree   loc: %p  bytes: %zu  total: %u \n",puser,bytes,zmalloc_tot);
 
329
   return;
 
330
}
 
331
 
 
332
 
 
333
//  Run a shell command and get its outputs one record at a time.
 
334
//  Start a new command with contx = 0. Do not change contx.
 
335
//  NULL return means no more output.
 
336
//  Get command exit status: err = command_status(contx)
 
337
//  Caller owns returned strings which are candidates for zfree()
 
338
//
 
339
//  strcat(buff," 2>&1");   //  combine stdout and stderr  v.2.14 removed
 
340
 
 
341
 
 
342
FILE *   CO_contx[10] = { 0,0,0,0,0,0,0,0,0,0 };
 
343
int      CO_status[10];
 
344
 
 
345
char * command_output(int &contx, const char *command, ...)                //  simplify, allow parallel usage
 
346
{                                                                          //       v.2.3
 
347
   FILE           *fid;
 
348
   va_list        arglist;
 
349
   char           buff[1000], *prec;
 
350
   
 
351
   if (contx == 0)                                                         //  start new command
 
352
   {
 
353
      for (contx = 1; contx < 10; contx++) 
 
354
         if (CO_contx[contx] == 0) break;
 
355
      if (contx == 10) zappcrash("command_output(), parallel usage > 9");
 
356
      
 
357
      va_start(arglist,command);                                           //  format command
 
358
      vsnprintf(buff,990,command,arglist);
 
359
      va_end(arglist);
 
360
      
 
361
      fid = popen(buff,"r");                                               //  execute command, output to FID
 
362
      if (fid == 0) {
 
363
         CO_status[contx] = errno;                                         //  failed to start
 
364
         return 0;
 
365
      }
 
366
      CO_contx[contx] = fid + 1000;
 
367
      CO_status[contx] = -1;                                               //  mark context busy
 
368
   }
 
369
 
 
370
   fid = CO_contx[contx] - 1000;
 
371
   prec = fgets_trim(buff,999,fid,1);                                      //  next output, less trailing \n
 
372
   if (prec) return strdupz(prec);                                         //  return output to caller
 
373
 
 
374
   CO_status[contx] = pclose(fid);                                         //  EOF, set status
 
375
   CO_contx[contx] = 0;                                                    //  mark context free
 
376
   return 0;
 
377
}
 
378
 
 
379
int command_status(int contx)                                              //  get command exit status
 
380
{
 
381
   int err = CO_status[contx];
 
382
   return WEXITSTATUS(err);                                                //  special BS for subprocess   v.2.3
 
383
}
 
384
 
 
385
 
 
386
//  signal a subprocess to pause, resume, or terminate
 
387
//  return:  0: OK  +-N: error
 
388
 
 
389
int signalProc(cchar *pname, cchar *signal)
 
390
{
 
391
   pid_t       pid;
 
392
   FILE        *fid;
 
393
   char        buff[100], *pp;
 
394
   int         err, ignore, nsignal = 0;
 
395
 
 
396
   sprintf(buff,"ps -C %s h o pid > /tmp/signalProc-temp",pname);
 
397
   err = system(buff);
 
398
   if (err) { err = 2; goto cleanup; }
 
399
   
 
400
   fid = fopen("/tmp/signalProc-temp","r");
 
401
   if (! fid) { err = 3; goto cleanup; }
 
402
 
 
403
   pp = fgets(buff,100,fid);
 
404
   fclose(fid);
 
405
   if (! pp) { err = 4; goto cleanup; }
 
406
 
 
407
   pid = atoi(buff);
 
408
   if (! pid) { err = 5; goto cleanup; }
 
409
 
 
410
   if (strEqu(signal,"pause")) nsignal = SIGSTOP; 
 
411
   if (strEqu(signal,"resume")) nsignal = SIGCONT; 
 
412
   if (strEqu(signal,"kill")) nsignal = SIGKILL; 
 
413
   err = kill(pid,nsignal);   
 
414
 
 
415
cleanup:
 
416
   ignore = system("rm -f /tmp/signalProc-temp");
 
417
   return err;
 
418
}
 
419
 
 
420
 
 
421
//  run a command or program as root user
 
422
//  sucomm:  root user access command, "su" or "sudo"
 
423
//  command:  shell command or filespec of the program to start
 
424
//  returns 0 if successfully started, else returns an error code
 
425
 
 
426
int runroot(const char *sucomm, const char *command)                       //  v.2.10
 
427
{
 
428
   char     xtcommand[500];
 
429
   int      err;
 
430
   
 
431
   if (strcmp(sucomm,"sudo") == 0)
 
432
   {
 
433
      snprintf(xtcommand,499,"xterm -geometry 40x3 -e sudo -S %s",command);
 
434
      err = system(xtcommand);
 
435
      return err;
 
436
   }
 
437
   
 
438
   if (strcmp(sucomm,"su") == 0)
 
439
   {
 
440
      snprintf(xtcommand,499,"xterm -geometry 40x3 -e su -c %s",command);
 
441
      err = system(xtcommand);
 
442
      return err;
 
443
   }
 
444
 
 
445
   return -1;
 
446
}
 
447
 
 
448
 
 
449
//  Check if a list of programs are all installed
 
450
//  If any are missing, pop-up a window of missing programs
 
451
//  Returns the number of missing programs (zero if none).
 
452
 
 
453
int checkinstall(const char *prog1, ...)  //  null terminated list         //  v.2.10
 
454
{
 
455
   va_list        arglist;
 
456
   char           *buff, errmessage[200] = "missing programs:\n";
 
457
   const char     *prog, *pp, *missprogs[20];
 
458
   int            contx, found, Nmiss = 0;
 
459
 
 
460
   va_start(arglist,prog1);
 
461
   prog = prog1;
 
462
 
 
463
   while (prog)
 
464
   {
 
465
      contx = 0;
 
466
      found = 0;
 
467
      
 
468
      while (true)
 
469
      {
 
470
         buff = command_output(contx,"whereis %s",prog);
 
471
         if (! buff) break;
 
472
         pp = strchr(buff,':');
 
473
         if (pp) pp = strchr(pp,'/');
 
474
         if (pp) found = 1;
 
475
         zfree(buff);
 
476
         continue;
 
477
      }
 
478
 
 
479
      if (! found) {
 
480
         if (Nmiss == 20) break;
 
481
         missprogs[Nmiss] = prog;
 
482
         Nmiss++;
 
483
      }
 
484
 
 
485
      prog = va_arg(arglist,const char *);
 
486
   }
 
487
   
 
488
   va_end(arglist);
 
489
   
 
490
   if (Nmiss) {
 
491
      for (int ii = 0; ii < Nmiss; ii++)
 
492
         strncatv(errmessage,199,missprogs[ii]," ",0);
 
493
      zmessageACK(errmessage);
 
494
   }
 
495
 
 
496
   return Nmiss;
 
497
}
 
498
 
 
499
 
 
500
//  fgets with additional feature: trailing \n \r are removed
 
501
//  optional bf flag: true if trailing blanks are to be removed
 
502
 
 
503
char * fgets_trim(char *buff, int maxcc, FILE *fid, int bf)
 
504
{
 
505
   int      cc;
 
506
   char     *pp;
 
507
   
 
508
   pp = fgets(buff,maxcc,fid);
 
509
   if (! pp) return pp;
 
510
   cc = strlen(buff);
 
511
   if (bf) while (cc && buff[cc-1] > 0 && buff[cc-1] <= ' ') --cc;         //  utf8  v.2.4
 
512
   else    while (cc && buff[cc-1] > 0 && buff[cc-1] < ' ') --cc;
 
513
   buff[cc] = 0;
 
514
   return pp;
 
515
}
 
516
 
 
517
 
 
518
//  return true if both files are in the same directory
 
519
//  both files may be files or directories
 
520
 
 
521
int samedirk(const char *file1, const char *file2)
 
522
{
 
523
   int         cc1, cc2;
 
524
   const char  *pp1, *pp2;                                                 //  v.2.15
 
525
   
 
526
   if (! file1 || ! file2) return 0;
 
527
   pp1 = strrchr(file1,'/');
 
528
   pp2 = strrchr(file2,'/');
 
529
   if (! pp1 && ! pp2) return 1;
 
530
   if (pp1 && ! pp2) return 0;
 
531
   if (! pp1 && pp2) return 0;
 
532
   cc1 = pp1 - file1;
 
533
   cc2 = pp2 - file2;
 
534
   if (cc1 != cc2) return 0;
 
535
   if (strncmp(file1,file2,cc1) == 0) return 1;
 
536
   return 0;
 
537
}
 
538
 
 
539
 
 
540
/**************************************************************************
 
541
 
 
542
   Parse a pathname (filespec) and return its components.
 
543
   Returned strings are allocated in static memory (no zfree needed).
 
544
   Missing components are returned as null pointers.
 
545
   
 
546
   input ppath         outputs
 
547
 
 
548
   /name1/name2/       directory /name1/name2/ with no file
 
549
   /name1/name2        directory /name1/name2/ if name2 a directory,
 
550
                       otherwise directory /name1/ and file name2
 
551
   /name1/name2.xxx    if .xxx < 8 chars, returns file name2 and ext .xxx,
 
552
                       otherwise returns file name2.xxx and no ext
 
553
 
 
554
   returns 0 if no error, else 1
 
555
   
 
556
***/
 
557
 
 
558
int parsefile(cchar *ppath, char **pdirk, char **pfile, char **pext)       //  v.2.15
 
559
{
 
560
   struct stat    statb;
 
561
   static char    dirk[1000], file[200], ext[8];
 
562
   char           *pp;
 
563
   int            err, cc1, cc2;
 
564
 
 
565
   *pdirk = *pfile = *pext = null;
 
566
   
 
567
   cc1 = strlen(ppath);
 
568
   if (cc1 > 999) return 1;                                                //  ppath too long
 
569
 
 
570
   strcpy(dirk,ppath);
 
571
   *pdirk = dirk;
 
572
   
 
573
   err = stat(dirk,&statb);                                                //  have directory only
 
574
   if (! err && S_ISDIR(statb.st_mode)) return 0;
 
575
   
 
576
   pp = (char *) strrchr(dirk,'/');
 
577
   if (! pp) return 1;                                                     //  illegal
 
578
 
 
579
   pp++;
 
580
   cc2 = pp - dirk;
 
581
   if (cc2 < 2 || cc2 == cc1) return 0;                                    //  have /xxxx  or  /xxxx/
 
582
   
 
583
   if (strlen(pp) > 199) return 1;                                         //  filename too long
 
584
 
 
585
   strcpy(file,pp);                                                        //  file part
 
586
   *pfile = file;
 
587
   *pp = 0;                                                                //  remove from dirk part
 
588
 
 
589
   pp = (char *) strrchr(file,'.');
 
590
   if (! pp || strlen(pp) > 7) return 0;                                   //  file part, no .ext
 
591
   
 
592
   strcpy(ext,pp);                                                         //  .ext part
 
593
   *pext = ext;
 
594
   *pp = 0;                                                                //  remove from file part
 
595
   return 0;
 
596
}
 
597
 
 
598
 
 
599
/**************************************************************************
 
600
 
 
601
   monitor a directory for file additions and deletions           v.2.16
 
602
 
 
603
   action:  open     Start monitoring directory
 
604
            event    Return next event or zero if none pending
 
605
            close    Stop monitoring
 
606
 
 
607
   dirk:  Input directory to monitor (action = open)
 
608
 
 
609
   file:  Output filename (action = event) relative to directory
 
610
          zfree() is called unless null
 
611
          zmalloc() is called to allocate new space
 
612
 
 
613
   returned status:
 
614
       -1   error
 
615
        0   OK (open, close), no event pending (event)
 
616
        1   file was possibly added
 
617
        2   file was deleted
 
618
        3   monitored directory is gone
 
619
        9   other
 
620
  
 
621
   NOTE  The Linux inotify() function is used and is not reliable
 
622
         for files renamed. The remove is reported but not the add.
 
623
         The 'other' event (with no file name) is reported.
 
624
 
 
625
***/
 
626
 
 
627
int zmondirk(const char *action, const char *dirk, char **file)
 
628
{
 
629
   struct inotify_event {
 
630
      int         wd;               //  Watch descriptor
 
631
      int         evt;              //  Event type
 
632
      int         cookie;           //  Associates related events (rename)
 
633
      int         len;              //  Size of file name field following
 
634
      char        fname[200];       //  null-terminated file name within directory
 
635
   };
 
636
   
 
637
   inotify_event     evbuff;
 
638
   struct timeval    waitime;
 
639
   fd_set            fids;
 
640
   int               wd, retval, cc, fcc;
 
641
   int               evbcc = sizeof(evbuff);
 
642
   static int        fid = -1;
 
643
   
 
644
   if (strEqu(action,"open"))                                              //  setup a monitored directory
 
645
   {
 
646
      fid = inotify_init();
 
647
      if (fid < 0) {
 
648
         return -1;
 
649
      }
 
650
      wd = inotify_add_watch(fid,dirk,IN_ALL_EVENTS);
 
651
      if (wd < 0) {
 
652
         close(fid);
 
653
         fid = -1;
 
654
         return -1;
 
655
      }
 
656
      return 0;
 
657
   }
 
658
   
 
659
   if (strEqu(action,"event"))                                             //  return pending event or zero
 
660
   {
 
661
      if (fid < 0) return -1;
 
662
 
 
663
      if (file) {
 
664
         if (*file) zfree(*file);                                          //  free prior memory
 
665
         *file = 0;
 
666
      }
 
667
 
 
668
      while (true)
 
669
      {
 
670
         FD_ZERO(&fids);
 
671
         FD_SET(fid, &fids);
 
672
         waitime.tv_sec = 0;
 
673
         waitime.tv_usec = 1;
 
674
 
 
675
         retval = select(fid+1, &fids, null, null, &waitime);
 
676
         if (retval == 0) return 0;                                        //  nothing pending
 
677
 
 
678
         if (retval == -1) {                                               //  error
 
679
            close(fid);
 
680
            fid = -1;
 
681
            return -1;
 
682
         }
 
683
 
 
684
         cc = read(fid,&evbuff,evbcc);                                     //  get pending event
 
685
         if (cc == -1) {
 
686
            close(fid);
 
687
            fid = -1;
 
688
            return -1;
 
689
         }
 
690
 
 
691
         fcc = evbuff.len;
 
692
         if (fcc > 199) return -1;
 
693
         if (fcc > 0 && file) *file = strdupz(evbuff.fname);               //  return filename
 
694
 
 
695
         if (evbuff.evt & (IN_CREATE + IN_MOVED_TO))                       //  file was added
 
696
            return 1;
 
697
         if (evbuff.evt & (IN_CLOSE_WRITE + IN_CLOSE_NOWRITE + IN_MODIFY)) //  file was possibly added
 
698
            return 1;
 
699
         if (evbuff.evt & (IN_DELETE + IN_MOVED_FROM))                     //  file was deleted
 
700
            return 2;
 
701
         if (evbuff.evt & (IN_DELETE_SELF + IN_MOVE_SELF))                 //  monitored directory gone
 
702
            return 3;
 
703
         return 9;                                                         //  other
 
704
      }
 
705
   }
 
706
   
 
707
   if (strEqu(action,"close"))                                             //  stop monitoring
 
708
   {
 
709
      if (fid > -1) retval = close(fid);
 
710
      else retval = -1;
 
711
      fid = -1;
 
712
      return retval;
 
713
   }
 
714
   
 
715
   zappcrash("zmondirk() call error");
 
716
   return -1;
 
717
}
 
718
 
 
719
 
 
720
/**************************************************************************
 
721
 
 
722
   utility to measure CPU time spent in various functions or code blocks        v.2.7
 
723
   
 
724
   cpu_profile_init()            initialize at start of test
 
725
   cpu_profile_report()          report CPU time per function
 
726
   cpu_profile_enter(fnum)       at entry to a function         //  inline, defined in zfuncs.h
 
727
   cpu_profile_exit(fnum)        at exit from a function        //  inline, defined in zfuncs.h
 
728
 
 
729
*/
 
730
 
 
731
volatile double   cpu_profile_table[100];
 
732
volatile double   cpu_profile_timer;
 
733
volatile double   cpu_profile_elapsed;
 
734
volatile int      cpu_profile_kill = 0;
 
735
 
 
736
void cpu_profile_init()
 
737
{
 
738
   void *   cpu_profile_timekeeper(void *);
 
739
 
 
740
   for (int ii = 0; ii < 99; ii++) 
 
741
      cpu_profile_table[ii] = 0;
 
742
   cpu_profile_elapsed = 0;
 
743
   start_detached_thread(cpu_profile_timekeeper,null);
 
744
}
 
745
 
 
746
void cpu_profile_report()
 
747
{
 
748
   cpu_profile_kill++;
 
749
 
 
750
   printf("elapsed: %.2f \n",cpu_profile_elapsed);
 
751
 
 
752
   for (int ii = 0; ii < 100; ii++)
 
753
   {
 
754
      double dtime = cpu_profile_table[ii];
 
755
      if (dtime) printf("cpu profile func: %d  time: %.2f \n",ii,dtime);
 
756
   }
 
757
}
 
758
 
 
759
void * cpu_profile_timekeeper(void *)
 
760
{
 
761
   timeval  time0, time1;
 
762
 
 
763
   gettimeofday(&time0,0);
 
764
 
 
765
   while (true)
 
766
   {
 
767
      gettimeofday(&time1,0);
 
768
      cpu_profile_elapsed = time1.tv_sec - time0.tv_sec
 
769
              + 0.000001 * (time1.tv_usec - time0.tv_usec);
 
770
      zsleep(0.001);
 
771
      if (cpu_profile_kill) break;
 
772
   }
 
773
   
 
774
   cpu_profile_kill = 0;
 
775
   return 0;
 
776
}
 
777
 
 
778
 
 
779
/**************************************************************************
 
780
    string utility functions
 
781
***************************************************************************
 
782
 
 
783
    strField()
 
784
 
 
785
    char * strField(const char *string, const char *delim, int Nth)
 
786
 
 
787
    Get the Nth field in input string, which contains at least N fields 
 
788
    delimited by the character(s) in delim (e.g. blank, comma).
 
789
    
 
790
    Returns a pointer to the found field (actually a pointer to a
 
791
    copy of the found field, with a null terminator appended).
 
792
    
 
793
    If a delimiter is immediately followed by another delimiter, it is 
 
794
    considered a field with zero length, and the string "" is returned.
 
795
 
 
796
    Leading blanks in a field are omitted from the returned field.
 
797
    A field with only blanks is returned as a single blank.
 
798
 
 
799
    The last field may be terminated by null or a delimiter.
 
800
    
 
801
    Characters within quotes (" or ') are treated as data within a
 
802
    field, i.e. blanks and delimiters are not processed as such.
 
803
    The quotes are removed from the returned field.
 
804
 
 
805
    If there are less than N fields, a null pointer is returned.
 
806
    
 
807
    The last 100 fields are saved and recycled in a circular manner.
 
808
    The caller does not have to free memory. If more memory depth is 
 
809
    needed, caller must copy the returned data elsewhere.
 
810
    
 
811
    The input string must be < 1000 characters.
 
812
    The output string may be modified if the length is not increased.
 
813
    
 
814
    Example: input string: ,a,bb,  cc,   ,dd"ee,ff"ggg,
 
815
             (first and last characters are comma)
 
816
             delimiter: comma
 
817
             Nth   returned string
 
818
              1:   (null string)
 
819
              2:   a
 
820
              3:   bb
 
821
              4:   cc
 
822
              5:   (one blank)
 
823
              6:   ddee,ffggg
 
824
              7:   (null pointer >> no more fields)
 
825
 
 
826
***************************************************************************/
 
827
 
 
828
const char * strField(const char *string, const char *delim, int Nth)
 
829
{
 
830
   static int     ftf = 1, nret = 0;
 
831
   static char    *retf[100]; 
 
832
   char           *pf1, pf2[1000];
 
833
   const char     quote1 = '\'', quote2 = '"';
 
834
   int            ii, nf, fcc = 0;
 
835
   static char    blankstring[2], nullstring[1];
 
836
   
 
837
   if (ftf)                                                                //  overall first call
 
838
   {
 
839
      ftf = 0;
 
840
      for (ii = 0; ii < 100; ii++) retf[ii] = 0;
 
841
      strcpy(blankstring," ");
 
842
      *nullstring = 0;
 
843
   }
 
844
 
 
845
   if (strlen(string) > 999) return 0;
 
846
   if (Nth < 1) return 0;
 
847
 
 
848
   pf1 = (char *) string - 1;                                              //  start parse
 
849
   nf = 0;
 
850
   
 
851
   while (nf < Nth)
 
852
   {
 
853
      pf1++;                                                               //  start field
 
854
      nf++;
 
855
      fcc = 0;
 
856
 
 
857
      while (*pf1 == ' ') pf1++;                                           //  skip leading blanks
 
858
 
 
859
      while (true)
 
860
      {
 
861
         if (*pf1 == quote1) {                                             //  pass chars between single quotes
 
862
            pf1++;                                                         //  (but without the quotes)
 
863
            while (*pf1 && *pf1 != quote1) pf2[fcc++] = *pf1++;
 
864
            if (*pf1 == quote1) pf1++;
 
865
         }
 
866
 
 
867
         else if (*pf1 == quote2) {                                        //  same for double quotes
 
868
            pf1++;
 
869
            while (*pf1 && *pf1 != quote2) pf2[fcc++] = *pf1++;
 
870
            if (*pf1 == quote2) pf1++;
 
871
         }
 
872
         
 
873
         else if (strchr(delim,*pf1) || *pf1 == 0) break;                  //  found delimiter or null
 
874
         
 
875
         else pf2[fcc++] = *pf1++;                                         //  pass normal character
 
876
      }
 
877
 
 
878
      if (*pf1 == 0) break;
 
879
   }
 
880
      
 
881
   if (nf < Nth) return 0;                                                 //  no Nth field
 
882
   
 
883
   if (fcc == 0) {                                                         //  empty field
 
884
      if (*string && pf1[-1] == ' ' && !strchr(delim,' '))                 //  all blanks and blank not delim.
 
885
         return blankstring;                                               //     return one blank
 
886
      if (*pf1 == 0) return 0;                                             //  no field
 
887
      return nullstring;                                                   //  return null string
 
888
   }
 
889
 
 
890
   if (++nret == 100) nret = 0;                                            //  use next return slot
 
891
   if (retf[nret]) zfree(retf[nret]);
 
892
   retf[nret] = zmalloc(fcc+2);
 
893
   strncpy0(retf[nret],pf2,fcc+1);
 
894
   return retf[nret];
 
895
}
 
896
 
 
897
const char * strField(const char *string, const char delim, int Nth)       //  alternative with one delimiter
 
898
{
 
899
   char     delims[2] = "x";
 
900
   
 
901
   *delims = delim;
 
902
   return strField(string,delims,Nth);
 
903
}
 
904
 
 
905
 
 
906
/**************************************************************************
 
907
 
 
908
   stat = strParms(begin, input, pname, maxcc, pval)
 
909
 
 
910
   Parse an input string with parameter names and values:
 
911
     "pname1=pval1 | pname2 | pname3=pval3 | pname4 ..."
 
912
   
 
913
   begin    int &          must be 1 to start new string, is modified
 
914
   input    const char *   input string
 
915
   pname    char *         output parameter name
 
916
   maxcc    int            max. length for pname, including null
 
917
   pval     double &       output parameter value
 
918
   stat     int            status: 0=OK, -1=EOL, 1=parse error
 
919
   
 
920
   Each call returns the next pname and pval.
 
921
   A pname with no pval is assigned a value of 1 (present).
 
922
   Input format:  pname1 | pname2=pval2 | pname3 ... null
 
923
   Leading blanks are ignored, and pnames may have imbedded blanks.
 
924
   pvals must convert to double using convSD (accepts decimal point or comma)
 
925
 
 
926
***************************************************************************/
 
927
 
 
928
int strParms(int &begin, const char *input, char *pname, int maxcc, double &pval)
 
929
{
 
930
   static int     ii, beginx = 3579246;
 
931
   const char     *pnamex, *delim;
 
932
   int            cc, err;
 
933
 
 
934
   if (begin == 1) {                                                       //  start new string
 
935
      begin = ++beginx;
 
936
      ii = 0;
 
937
   }
 
938
 
 
939
   if (begin != beginx) zappcrash("strParms call error");                  //  thread safe, not reentrant
 
940
   
 
941
   *pname = 0;                                                             //  initz. outputs to nothing
 
942
   pval = 0;
 
943
   
 
944
   while (input[ii] == ' ') ii++;                                          //  skip leading blanks
 
945
   if (input[ii] == 0) return -1;                                          //  no more data
 
946
 
 
947
   pnamex = input + ii;                                                    //  next pname
 
948
   
 
949
   for (cc = 0; ; cc++)
 
950
   {                                                                       //  look for delimiter
 
951
      if (pnamex[cc] == '=') break;
 
952
      if (pnamex[cc] == '|') break;
 
953
      if (pnamex[cc] == 0) break;
 
954
   }
 
955
   
 
956
   if (cc == 0) return 1;                                                  //  err: 2 delimiters
 
957
   if (cc >= maxcc) return 1;                                              //  err: pname too big
 
958
 
 
959
   strncpy0(pname,pnamex,cc+1);                                            //  pname >> caller
 
960
   strTrim(pname);                                                         //  remove trailing blanks
 
961
 
 
962
   if (pnamex[cc] == 0) {                                                  //  pname + null
 
963
      ii += cc;                                                            //  position for next call
 
964
      pval = 1.0;                                                          //  pval = 1 >> caller
 
965
      return 0;
 
966
   }
 
967
 
 
968
   if (pnamex[cc] == '|') {                                                //  pname + |
 
969
      ii += cc + 1;                                                        //  position for next call
 
970
      pval = 1.0;                                                          //  pval = 1 >> caller
 
971
      return 0;
 
972
   }
 
973
 
 
974
   ii += cc + 1;                                                           //  pname = pval
 
975
   err = convSD(input + ii, pval, &delim);                                 //  parse pval   (was strtod()
 
976
   if (err > 1) return 1;
 
977
   while (*delim == ' ') delim++;                                          //  skip poss. trailing blanks
 
978
   if (*delim && *delim != '|') return 1;                                  //  err: delimiter not | or null
 
979
   ii = delim - input;
 
980
   if (*delim) ii++;                                                       //  position for next call
 
981
   return 0;
 
982
}
 
983
 
 
984
 
 
985
//  Produce random value from hashed input string.
 
986
//  Output range is 0 to max-1.
 
987
 
 
988
int strHash(const char *string, int max)
 
989
{
 
990
   int      hash, ii, cc;
 
991
   long     *lstring = (long *) string;
 
992
 
 
993
   cc = strlen(string);
 
994
   if (cc > 99) appcrash("strHash, too long",null);
 
995
 
 
996
   cc = (cc + 3) / 4;
 
997
   hash = 0x12345678;
 
998
 
 
999
   for (ii = 0; ii < cc; ii++) {
 
1000
      hash = hash ^ lstring[ii];
 
1001
      hash = hash ^ (hash << 5);
 
1002
      hash = hash ^ (hash >> 7);
 
1003
      hash = hash ^ (hash << 9);
 
1004
      hash = hash ^ (hash >> 11);
 
1005
   }
 
1006
 
 
1007
   hash = hash & 0x3FFFFFFF;
 
1008
   return (hash % max);
 
1009
}
 
1010
 
 
1011
 
 
1012
//  Hash an input string into a random printable (a-z) output string.
 
1013
//  Returns outcc character random printable string in static memory.
 
1014
//  Every output character is randomized from the entire input string.
 
1015
 
 
1016
const char * strHash2(const char *instring, int outcc)
 
1017
{
 
1018
   int            incc, ii, jj, rani = 0;
 
1019
   int64          seed = 13579;
 
1020
   static char    outstring[40];
 
1021
 
 
1022
   incc = strlen(instring);
 
1023
   if (outcc > 39) appcrash("strHash2() outcc > 39");
 
1024
   
 
1025
   for (ii = 0; ii < outcc; ii++)
 
1026
   {
 
1027
      for (jj = 0; jj < incc; jj++)
 
1028
      {
 
1029
         seed = seed + instring[jj];
 
1030
         rani = lrandz(&seed);
 
1031
      }
 
1032
      outstring[ii] = 'a' + rani % 26;
 
1033
   }
 
1034
 
 
1035
   outstring[ii] = 0;
 
1036
   return outstring;
 
1037
}
 
1038
 
 
1039
 
 
1040
//  Copy string with specified max. length (including null terminator).
 
1041
//  truncate if needed. null terminator is always supplied.
 
1042
 
 
1043
int strncpy0(char *dest, const char *source, uint cc)
 
1044
{
 
1045
   strncpy(dest,source,cc);
 
1046
   dest[cc-1] = 0;
 
1047
   if (strlen(source) >= cc) return 1;                                     //  truncated  v.2.4
 
1048
   else return 0;
 
1049
}
 
1050
 
 
1051
 
 
1052
//  Copy string with blank pad to specified length.  No null.
 
1053
 
 
1054
void strnPad(char *dest, const char *source, int cc)
 
1055
{
 
1056
   strncpy(dest,source,cc);
 
1057
   int ii = strlen(source);
 
1058
   for (int jj = ii; jj < cc; jj++) dest[jj] = ' ';
 
1059
}
 
1060
 
 
1061
 
 
1062
//  Remove trailing blanks from a string. Returns remaining length.
 
1063
 
 
1064
int strTrim(char *dest, const char *source)
 
1065
{
 
1066
   if (dest != source) strcpy(dest,source);
 
1067
   return strTrim(dest);
 
1068
}
 
1069
 
 
1070
int strTrim(char *dest)
 
1071
{
 
1072
   int  ii = strlen(dest);
 
1073
   while (ii && (dest[ii-1] == ' ')) dest[--ii] = 0;
 
1074
   return ii;
 
1075
}
 
1076
 
 
1077
 
 
1078
//  Remove leading and trailing blanks from a string. 
 
1079
//  Returns remaining length, possibly zero.
 
1080
 
 
1081
int strTrim2(char *dest, const char *source)                               //  v.2.4
 
1082
{
 
1083
   const char  *pp1, *pp2;
 
1084
   int         cc;
 
1085
 
 
1086
   pp1 = source;
 
1087
   pp2 = source + strlen(source) - 1;
 
1088
   while (*pp1 == ' ') pp1++;
 
1089
   while (*pp2 == ' ' && pp2 > pp1) pp2--;
 
1090
   cc = pp2 - pp1 + 1;
 
1091
   strncpy(dest,pp1,cc);
 
1092
   dest[cc] = 0;
 
1093
   return cc;
 
1094
}
 
1095
 
 
1096
 
 
1097
//  Remove all blanks from a string. Returns remaining length.
 
1098
 
 
1099
int strCompress(char *dest, const char *source)
 
1100
{
 
1101
   if (dest != source) strcpy(dest,source);
 
1102
   return strCompress(dest);
 
1103
}
 
1104
 
 
1105
int strCompress(char *string)
 
1106
{
 
1107
   int   ii, jj;
 
1108
 
 
1109
   for (ii = jj = 0; string[ii]; ii++)
 
1110
   {
 
1111
      if (string[ii] != ' ')
 
1112
      {
 
1113
         string[jj] = string[ii];
 
1114
         jj++;
 
1115
      }
 
1116
   }
 
1117
   string[jj] = 0;
 
1118
   return jj;
 
1119
}
 
1120
 
 
1121
 
 
1122
//  Concatenate multiple strings, staying within a specified overall length.
 
1123
//  The destination string is also the first source string. 
 
1124
//  Null marks the end of the source strings (omission --> crash).
 
1125
//  Output is truncated to fit within the specified length.
 
1126
//  A final null is assured and is included in the length.
 
1127
//  Returns 0 if OK, 1 if truncation was needed.
 
1128
 
 
1129
int strncatv(char *dest, int maxcc, const char *source, ...)
 
1130
{
 
1131
   const char  *ps;
 
1132
   va_list     arglist;
 
1133
 
 
1134
   maxcc = maxcc - strlen(dest) - 1;
 
1135
   if (maxcc < 0) return 1;
 
1136
   va_start(arglist,source);
 
1137
   ps = source;
 
1138
 
 
1139
   while (ps)
 
1140
   {
 
1141
      strncat(dest,ps,maxcc);
 
1142
      maxcc = maxcc - strlen(ps);
 
1143
      if (maxcc < 0) break;
 
1144
      ps = va_arg(arglist,const char *);
 
1145
   }
 
1146
   
 
1147
   va_end(arglist);
 
1148
   if (maxcc < 0) return 1;
 
1149
   return 0;
 
1150
}
 
1151
 
 
1152
 
 
1153
//  Match 1st string to N additional strings.
 
1154
//  Return matching string number 1 to N or 0 if no match.
 
1155
//  Supply a null argument for end of list.
 
1156
 
 
1157
int strcmpv(const char *string, ...)
 
1158
{
 
1159
   int      match = 0;
 
1160
   char     *stringN;
 
1161
   va_list  arglist;
 
1162
 
 
1163
   va_start(arglist,string);
 
1164
 
 
1165
   while (1)
 
1166
   {
 
1167
      stringN = va_arg(arglist, char *);
 
1168
      if (stringN == null)
 
1169
      {
 
1170
         va_end(arglist);
 
1171
         return 0;
 
1172
      }
 
1173
 
 
1174
      match++;
 
1175
      if (strcmp(string,stringN) == 0)
 
1176
      {
 
1177
         va_end(arglist);
 
1178
         return match;
 
1179
      }
 
1180
   }
 
1181
}
 
1182
 
 
1183
 
 
1184
//  convert string to upper case
 
1185
 
 
1186
void strToUpper(char *string)
 
1187
{
 
1188
   int         ii;
 
1189
   char        jj;
 
1190
   const int   delta = 'A' - 'a';
 
1191
 
 
1192
   for (ii = 0; (jj = string[ii]); ii++)
 
1193
        if ((jj >= 'a') && (jj <= 'z')) string[ii] += delta;
 
1194
}
 
1195
 
 
1196
void strToUpper(char *dest, const char *source)
 
1197
{
 
1198
   strcpy(dest,source);
 
1199
   strToUpper(dest);
 
1200
}
 
1201
 
 
1202
 
 
1203
//  convert string to lower case
 
1204
 
 
1205
void strToLower(char *string)
 
1206
{
 
1207
   int         ii;
 
1208
   char        jj;
 
1209
   const int   delta = 'a' - 'A';
 
1210
 
 
1211
   for (ii = 0; (jj = string[ii]); ii++)
 
1212
        if ((jj >= 'A') && (jj <= 'Z')) string[ii] += delta;
 
1213
}
 
1214
 
 
1215
void strToLower(char *dest, const char *source)
 
1216
{
 
1217
   strcpy(dest,source);
 
1218
   strToLower(dest);
 
1219
}
 
1220
 
 
1221
 
 
1222
//  copy string strin to strout, replacing every occurrence
 
1223
//    of the substring ssin with the substring ssout
 
1224
 
 
1225
int repl_1str(cchar *strin, char *strout, cchar *ssin, cchar *ssout)
 
1226
{
 
1227
   int         ccc, cc1, cc2, nfound;
 
1228
   const char  *ppp;                                                       //  v.2.15
 
1229
   
 
1230
   cc1 = strlen(ssin);
 
1231
   cc2 = strlen(ssout);
 
1232
   nfound = 0;
 
1233
   
 
1234
   while ((ppp = strstr(strin,ssin)))
 
1235
   {
 
1236
      nfound++;
 
1237
      ccc = ppp - strin;
 
1238
      strncpy(strout,strin,ccc);
 
1239
      strout += ccc;
 
1240
      strin += ccc;
 
1241
      strncpy(strout,ssout,cc2);
 
1242
      strin += cc1;
 
1243
      strout += cc2;
 
1244
   }
 
1245
 
 
1246
   strcpy(strout,strin);
 
1247
   return nfound;
 
1248
}
 
1249
 
 
1250
 
 
1251
//  like repl_1str, but multiple pairs of substrings are processed
 
1252
//   (... ssin1, ssout1, ssin2, ssout2, ... null) 
 
1253
 
 
1254
int repl_Nstrs(cchar *strin, char *strout, ...)
 
1255
{
 
1256
   va_list     arglist;
 
1257
   cchar       *ssin, *ssout;
 
1258
   char        ftemp[maxfcc];
 
1259
   int         ftf, nfound;
 
1260
   
 
1261
   ftf = 1;
 
1262
   nfound = 0;
 
1263
   va_start(arglist,strout);
 
1264
   
 
1265
   while (true)
 
1266
   {
 
1267
      ssin = va_arg(arglist, char *);
 
1268
      if (! ssin) break;
 
1269
      ssout = va_arg(arglist, char *);
 
1270
 
 
1271
      if (ftf) {
 
1272
         ftf = 0;
 
1273
         nfound += repl_1str(strin,strout,ssin,ssout);
 
1274
      }
 
1275
 
 
1276
      else {
 
1277
         strcpy(ftemp,strout);
 
1278
         nfound += repl_1str(ftemp,strout,ssin,ssout);
 
1279
      }
 
1280
   }
 
1281
 
 
1282
   va_end(arglist);
 
1283
   return nfound;
 
1284
}
 
1285
 
 
1286
 
 
1287
//  Copy and convert string to hex string.
 
1288
//  Each input character 'A' >> 3 output characters "41 "
 
1289
 
 
1290
void strncpyx(char *out, const char *in, int ccin)
 
1291
{
 
1292
   int      ii, jj, c1, c2;
 
1293
   char     cx[] = "0123456789ABCDEF";
 
1294
 
 
1295
   if (! ccin) ccin = strlen(in);
 
1296
 
 
1297
   for (ii = 0, jj = 0; ii < ccin; ii++, jj += 3)
 
1298
   {
 
1299
      c1 = (uchar) in[ii] >> 4;
 
1300
      c2 = in[ii] & 15;
 
1301
      out[jj] = cx[c1];
 
1302
      out[jj+1] = cx[c2];
 
1303
      out[jj+2] = ' ';
 
1304
   }
 
1305
   out[jj] = 0;
 
1306
   return;
 
1307
}
 
1308
 
 
1309
 
 
1310
//  Strip trailing zeros from ascii floating numbers
 
1311
//    (e.g. 1.230000e+02  -->  1.23e+02)
 
1312
 
 
1313
void StripZeros(char *pNum)
 
1314
{
 
1315
   int     ii, ll;
 
1316
   int     pp, k1, k2;
 
1317
   char    work[20];
 
1318
 
 
1319
   ll = strlen(pNum);
 
1320
   if (ll >= 20) return;
 
1321
 
 
1322
   for (ii = 0; ii < ll; ii++)
 
1323
   {
 
1324
      if (pNum[ii] == '.')
 
1325
      {
 
1326
         pp = ii;
 
1327
         k1 = k2 = 0;
 
1328
         for (++ii; ii < ll; ii++)
 
1329
         {
 
1330
            if (pNum[ii] == '0')
 
1331
            {
 
1332
               if (! k1) k1 = k2 = ii;
 
1333
               else k2 = ii;
 
1334
               continue;
 
1335
            }
 
1336
 
 
1337
            if ((pNum[ii] >= '1') && (pNum[ii] <= '9'))
 
1338
            {
 
1339
               k1 = 0;
 
1340
               continue;
 
1341
            }
 
1342
 
 
1343
            break;
 
1344
         }
 
1345
 
 
1346
         if (! k1) return;
 
1347
 
 
1348
         if (k1 == pp + 1) k1++;
 
1349
         if (k2 < k1) return;
 
1350
         strcpy(work,pNum);
 
1351
         strcpy(work+k1,pNum+k2+1);
 
1352
         strcpy(pNum,work);
 
1353
         return;
 
1354
      }
 
1355
   }
 
1356
}
 
1357
 
 
1358
 
 
1359
//  test for blank/null string
 
1360
 
 
1361
int blank_null(const char *string)
 
1362
{
 
1363
   if (! string) return 1;                                                 //  null string
 
1364
   if (! *string) return 2;                                                //  zero length string
 
1365
   int cc = strlen(string);
 
1366
   for (int ii = 0; ii < cc; ii++)
 
1367
      if (string[ii] != ' ') return 0;                                     //  non-blank string
 
1368
   return 3;                                                               //  blank string
 
1369
}
 
1370
 
 
1371
 
 
1372
//  make a copy of a string in non-volatile (heap) memory
 
1373
//  returned string is subject for zfree();
 
1374
 
 
1375
char * strdupz(const char *string, int more)
 
1376
{
 
1377
   char  *pp = zmalloc(strlen(string)+1+more);
 
1378
   strcpy(pp,string);
 
1379
   return pp;
 
1380
}
 
1381
 
 
1382
 
 
1383
//  copy into existing 'zmalloc' string if present and long enough         //  v.2.8
 
1384
//  else free memory and allocate a longer one
 
1385
//  destination string is subject for zfree()
 
1386
 
 
1387
int strdupz(const char *source, char *&zdest, int more)
 
1388
{
 
1389
   int      ccs, ccd;
 
1390
 
 
1391
   ccs = strlen(source);
 
1392
   if (! zdest) zdest = zmalloc(ccs+1+more);
 
1393
   ccd = (int) *(zdest-8);
 
1394
   if (ccd < ccs+1) {
 
1395
      zfree(zdest);
 
1396
      zdest = zmalloc(ccs+1+more);
 
1397
   }
 
1398
   strcpy(zdest,source);
 
1399
   return ccs;
 
1400
 
1401
 
 
1402
 
 
1403
//  clean \x escape sequences and replace them with the escaped character
 
1404
//    \n >> newline  \" >> doublequote  \\ >> backslash   etc.
 
1405
//  see  $ man ascii  for the complete list
 
1406
 
 
1407
int clean_escapes(char *string)
 
1408
{
 
1409
   char     *pp1 = string, *pp2 = string, *pp;
 
1410
   char     char1;
 
1411
   char     escapes[] = "abtnvfr";
 
1412
   int      count = 0;
 
1413
   
 
1414
   while (true)
 
1415
   {
 
1416
      char1 = *pp1++;
 
1417
 
 
1418
      if (char1 == 0) {
 
1419
         *pp2 = 0;
 
1420
         return count;
 
1421
      }
 
1422
 
 
1423
      else if (char1 == '\\')  {
 
1424
         char1 = *pp1++;
 
1425
         pp = strchr(escapes,char1);
 
1426
         if (pp) char1 = pp - escapes + 7;
 
1427
         count++;
 
1428
      }
 
1429
 
 
1430
      *pp2++ = char1;
 
1431
   }
 
1432
}
 
1433
 
 
1434
 
 
1435
//  Compute the graphic character count for a UTF8 character string.
 
1436
//  Depends on UTF8 rules:
 
1437
//    - ascii characters are positive (actually 0x00 to 0x7F)
 
1438
//    - 1st byte of multibyte sequence is negative (actually 0xC0 to 0xFD)
 
1439
//    - subsequent bytes are negative and < 0xC0 (actually 0x80 to 0xBF)
 
1440
 
 
1441
int utf8len(const char *utf8string)                                        //  v.2.3
 
1442
{
 
1443
   int      ii, cc;
 
1444
   char     xlimit = 0xC0;
 
1445
 
 
1446
   for (ii = cc = 0; utf8string[ii]; ii++)
 
1447
   {
 
1448
      if (utf8string[ii] < 0)                                              //  multibyte character
 
1449
         while (utf8string[ii+1] < xlimit) ii++;                           //  skip extra bytes
 
1450
      cc++;
 
1451
   }
 
1452
 
 
1453
   return cc;
 
1454
}
 
1455
 
 
1456
 
 
1457
//  Extract a UTF8 substring with a specified count of graphic characters.
 
1458
//    utf8in     input UTF8 string
 
1459
//    utf8out    output UTF8 string, which must be long enough 
 
1460
//    pos        initial graphic character position to get (0 = first)
 
1461
//    cc         max. count of graphic characters to get
 
1462
//    returns    number of graphic characters extracted, <= cc
 
1463
//  Output string is null terminated after last extracted character.
 
1464
 
 
1465
int utf8substring(char *utf8out, const char *utf8in, int pos, int cc)      //  v.2.3
 
1466
{
 
1467
   int      ii, jj, kk, posx, ccx;
 
1468
   char     xlimit = 0xC0;
 
1469
 
 
1470
   for (ii = posx = 0; posx < pos && utf8in[ii]; ii++)
 
1471
   {
 
1472
      if (utf8in[ii] < 0)
 
1473
         while (utf8in[ii+1] < xlimit) ii++;
 
1474
      posx++;
 
1475
   }
 
1476
 
 
1477
   jj = ii;   
 
1478
 
 
1479
   for (ccx = 0; ccx < cc && utf8in[jj]; jj++)
 
1480
   {
 
1481
      if (utf8in[jj] < 0)
 
1482
         while (utf8in[jj+1] < xlimit) jj++;
 
1483
      ccx++;
 
1484
   }
 
1485
   
 
1486
   kk = jj - ii;
 
1487
 
 
1488
   strncpy(utf8out,utf8in+ii,kk);
 
1489
   utf8out[kk] = 0;
 
1490
   
 
1491
   return   ccx;
 
1492
}
 
1493
 
 
1494
 
 
1495
//  check a string for valid utf8 encoding
 
1496
//  returns:  0 = OK,  1 = bad string
 
1497
 
 
1498
int utf8_check(const char *string)                                         //  v.2.4
 
1499
{
 
1500
   const char        *pp;
 
1501
   unsigned char     ch1, ch2, nch;
 
1502
   
 
1503
   for (pp = string; *pp; pp++)
 
1504
   {
 
1505
      ch1 = *pp;
 
1506
      if (ch1 < 0x7F) continue;
 
1507
      if (ch1 > 0xBF && ch1 < 0xE0) nch = 1;
 
1508
      else if (ch1 < 0xF0) nch = 2;
 
1509
      else if (ch1 < 0xF8) nch = 3;
 
1510
      else if (ch1 < 0xFC) nch = 4;
 
1511
      else if (ch1 < 0xFE) nch = 5;
 
1512
      else return 1;
 
1513
      while (nch) {
 
1514
         pp++;
 
1515
         ch2 = *pp;
 
1516
         if (ch2 < 0x80 || ch2 > 0xBF) return 1;
 
1517
         nch--;
 
1518
      }
 
1519
   }
 
1520
 
 
1521
   return 0;
 
1522
}
 
1523
 
 
1524
 
 
1525
//  Find the Nth graphic character position within a UTF8 string
 
1526
//    utf8in      input UTF8 string
 
1527
//    Nth         graphic character position, zero based
 
1528
//  returns starting character (byte) position of Nth graphic character
 
1529
 
 
1530
int utf8_position(const char *utf8in, int Nth)                             //  v.2.4
 
1531
{
 
1532
   int      ii, posx;
 
1533
   char     xlimit = 0xC0;
 
1534
 
 
1535
   for (ii = posx = 0; posx < Nth && utf8in[ii]; ii++)
 
1536
   {
 
1537
      if (utf8in[ii] < 0)
 
1538
         while (utf8in[ii+1] < xlimit) ii++;
 
1539
      posx++;
 
1540
   }
 
1541
 
 
1542
   return  ii;
 
1543
}
 
1544
 
 
1545
 
 
1546
/**************************************************************************
 
1547
   bitmap functions
 
1548
***************************************************************************/
 
1549
 
 
1550
//  create a new bitmap with specified bit length. 
 
1551
//  initially all bits are false.
 
1552
 
 
1553
bitmap * bitmap_new(int nbits)
 
1554
{
 
1555
   int      cc, ii;
 
1556
   bitmap   *bm;
 
1557
   
 
1558
   bm = (bitmap *) zmalloc(sizeof(bitmap));
 
1559
   bm->nbits = nbits;
 
1560
   cc = (nbits + 7) / 8;
 
1561
   bm->bits = (uchar *) zmalloc(cc);
 
1562
   for (ii = 0; ii < cc; ii++) bm->bits[ii] = 0;
 
1563
   return bm;
 
1564
}
 
1565
 
 
1566
 
 
1567
//  set bit in bitmap to true or false
 
1568
 
 
1569
void bitmap_set(bitmap *bm, int bit, bool value)
 
1570
{
 
1571
   int      ii, jj;
 
1572
   uchar    bit1;
 
1573
 
 
1574
   if (bit >= bm->nbits) zappcrash("bitmap, bit %d too big",bit);
 
1575
   ii = bit / 8;
 
1576
   jj = bit % 8;
 
1577
   bit1 = 0x80 >> jj;
 
1578
 
 
1579
   if (value) bm->bits[ii] = bm->bits[ii] | bit1;
 
1580
   else {
 
1581
      bit1 = bit1 ^ 0xff;
 
1582
      bm->bits[ii] = bm->bits[ii] & bit1;
 
1583
   }
 
1584
 
 
1585
   return;
 
1586
}
 
1587
 
 
1588
 
 
1589
//  fetch bitmap bit, return true or false
 
1590
 
 
1591
bool bitmap_get(bitmap *bm, int bit)
 
1592
{
 
1593
   int      ii, jj;
 
1594
   uchar    bit1;
 
1595
 
 
1596
   ii = bit / 8;
 
1597
   jj = bit % 8;
 
1598
   bit1 = bm->bits[ii] << jj;
 
1599
   if (bit1 < 127) return false;
 
1600
   else return true;
 
1601
}
 
1602
 
 
1603
 
 
1604
//  delete bitmap
 
1605
 
 
1606
void bitmap_delete(bitmap *bm)
 
1607
{
 
1608
   zfree(bm->bits);
 
1609
   zfree(bm);
 
1610
   return;
 
1611
}
 
1612
 
 
1613
 
 
1614
/**************************************************************************
 
1615
   variable list functions - array / list of strings
 
1616
***************************************************************************/
 
1617
 
 
1618
//  create new variable list with specified capacity
 
1619
 
 
1620
pvlist * pvlist_create(int max)
 
1621
{
 
1622
   pvlist      *pv;
 
1623
 
 
1624
   pv = (pvlist *) zmalloc(sizeof(pvlist));
 
1625
   pv->max = max;
 
1626
   pv->act = 0;
 
1627
   pv->list = (char **) zmalloc(max * sizeof(char *));
 
1628
   return pv;
 
1629
}
 
1630
 
 
1631
//  free memory for variable list
 
1632
   
 
1633
void pvlist_free(pvlist *pv)
 
1634
{
 
1635
   int      ii;
 
1636
   
 
1637
   for (ii = 0; ii < pv->act; ii++)
 
1638
      zfree(pv->list[ii]);
 
1639
   zfree(pv->list);
 
1640
   zfree(pv);
 
1641
}
 
1642
 
 
1643
//  append new entry to end of list (optional if unique)
 
1644
//  if list if full, first entry is removed and rest are packed down
 
1645
//  return: N >= 0: new entry added at position N
 
1646
//          N = -1: not unique, not added
 
1647
   
 
1648
int pvlist_append(pvlist *pv, const char *entry, int unique)
 
1649
{
 
1650
   int      ii;
 
1651
 
 
1652
   if (unique && pvlist_find(pv,entry) >= 0) return -1;                    //  not unique
 
1653
 
 
1654
   if (pv->act == pv->max) pvlist_remove(pv,0);                            //  if list full, remove 1st entry
 
1655
 
 
1656
   ii = pv->act;
 
1657
   pv->list[ii] = strdupz(entry);                                          //  add to end of list
 
1658
   pv->act++;
 
1659
   return ii;
 
1660
}
 
1661
 
 
1662
//  prepend new entry to list (optional if unique)
 
1663
//  prior list entries are pushed down to make room
 
1664
//  if list is full, last entry is removed first
 
1665
//  return: N = 0: new entry added at position 0
 
1666
//          N = -1: not unique, not added
 
1667
   
 
1668
int pvlist_prepend(pvlist *pv, const char *entry, int unique)
 
1669
{
 
1670
   int      ii;
 
1671
   
 
1672
   if (unique && pvlist_find(pv,entry) >= 0) return -1;                    //  not unique
 
1673
 
 
1674
   if (pv->act == pv->max) pvlist_remove(pv,pv->act-1);                    //  if list full, remove last entry
 
1675
 
 
1676
   for (ii = pv->act; ii > 0; ii--)                                        //  push all list entries down
 
1677
      pv->list[ii] = pv->list[ii-1];
 
1678
   pv->list[0] = strdupz(entry);                                           //  add to start of list
 
1679
   pv->act++;
 
1680
   return 0;
 
1681
}
 
1682
 
 
1683
//  find list entry by name, return -1 if not found
 
1684
   
 
1685
int pvlist_find(pvlist *pv, const char *entry)
 
1686
{
 
1687
   int      ii;
 
1688
 
 
1689
   for (ii = 0; ii < pv->act; ii++)
 
1690
      if (strEqu(entry,pv->list[ii])) break;
 
1691
   if (ii < pv->act) return ii;
 
1692
   return -1;
 
1693
}
 
1694
 
 
1695
//  remove an entry by name and repack list
 
1696
   
 
1697
int pvlist_remove(pvlist *pv, const char *entry)
 
1698
{
 
1699
   int      ii;
 
1700
   
 
1701
   ii = pvlist_find(pv,entry);
 
1702
   if (ii < 0) return -1;
 
1703
   pvlist_remove(pv,ii);
 
1704
   return ii;
 
1705
}
 
1706
 
 
1707
//  remove an entry by number and repack list
 
1708
   
 
1709
int pvlist_remove(pvlist *pv, int ii)
 
1710
{
 
1711
   if (ii < 0 || ii >= pv->act) return -1;
 
1712
   zfree(pv->list[ii]);
 
1713
   for (++ii; ii < pv->act; ii++)
 
1714
      pv->list[ii-1] = pv->list[ii];
 
1715
   pv->act--;
 
1716
   return 0;
 
1717
}
 
1718
 
 
1719
 
 
1720
//  return entry count
 
1721
 
 
1722
int pvlist_count(pvlist *pv)
 
1723
{
 
1724
   return pv->act;
 
1725
}
 
1726
 
 
1727
 
 
1728
//  replace Nth entry with new one
 
1729
 
 
1730
int pvlist_replace(pvlist * pv, int ii, char *entry)
 
1731
{
 
1732
   if (ii < 0 || ii >= pv->act) return -1;
 
1733
   zfree(pv->list[ii]);
 
1734
   pv->list[ii] = strdupz(entry);
 
1735
   return 0;
 
1736
}
 
1737
 
 
1738
 
 
1739
//  return Nth entry or null
 
1740
 
 
1741
char * pvlist_get(pvlist *pv, int Nth)
 
1742
{
 
1743
   if (Nth >= pv->act) return 0;
 
1744
   return pv->list[Nth];
 
1745
}
 
1746
 
 
1747
 
 
1748
//  sort list in ascending order
 
1749
 
 
1750
int pvlist_sort(pvlist *pv)
 
1751
{
 
1752
   HeapSort(pv->list,pv->act);
 
1753
   return 0;
 
1754
}
 
1755
 
 
1756
 
 
1757
/**************************************************************************
 
1758
 
 
1759
   Conversion Utilities
 
1760
 
 
1761
   convSI(string, inum, delim)                     string to int
 
1762
   convSI(string, inum, low, high, delim)          string to int with range check
 
1763
 
 
1764
   convSD(string, dnum, delim)                     string to double
 
1765
   convSD(string, dnum, low, high, delim)          string to double with range check
 
1766
 
 
1767
   convIS(inum, string, cc)                        int to string with returned cc
 
1768
 
 
1769
   convDS(fnum, digits, string, cc)                double to string with specified 
 
1770
                                                     digits of precision and returned cc
 
1771
   
 
1772
   string      input (const char *) or output (char *)
 
1773
   inum        input (int) or output (int &)
 
1774
   dnum        input (double) or output (double &)
 
1775
   delim       optional output pointer to delimiter (null or const char **)
 
1776
   low, high   input range check (int or double)
 
1777
   cc          output string length (int &)
 
1778
   digits      input digits of precision (int) to be used for output string
 
1779
 
 
1780
   function status returned:
 
1781
       0    normal conversion, no invalid digits, blank/null termination
 
1782
       1    successful converstion, but trailing non-numeric found
 
1783
       2    conversion OK, but outside specified limits
 
1784
       3    null or blank string, converted to zero
 
1785
       4    conversion error, invalid data in string
 
1786
   overlapping statuses have following precedence: 4 3 2 1 0
 
1787
 
 
1788
***************************************************************************/
 
1789
 
 
1790
#define  max10  (0x7fffffff / 10)
 
1791
 
 
1792
 
 
1793
//  Convert string to integer
 
1794
 
 
1795
int convSI(const char *string, int &inum, const char **delim)
 
1796
{
 
1797
   char        ch;
 
1798
   int         sign = 0, digits = 0, tnb = 0;
 
1799
   const char  *pch = string;
 
1800
 
 
1801
   inum = 0;
 
1802
 
 
1803
   while ((ch = *pch) == ' ') pch++;                                       //  skip leading blanks
 
1804
 
 
1805
   if (ch == '-') sign = -1;                                               //  process leading +/- sign
 
1806
   if (ch == '+') sign = 1;                                                //  (at most one sign character)
 
1807
   if (sign) pch++;
 
1808
 
 
1809
   while ((*pch >= '0') && (*pch <= '9'))                                  //  process digits 0 - 9
 
1810
   {
 
1811
      if (inum > max10) goto conv_err;                                     //  value too big
 
1812
      inum = 10 * inum + *pch - '0';
 
1813
      digits++;
 
1814
      pch++;
 
1815
   }
 
1816
 
 
1817
   if (delim) *delim = pch;                                                //  terminating delimiter
 
1818
   if (*pch && (*pch != ' ')) tnb++;                                       //  not null or blank
 
1819
 
 
1820
   if (! digits)                                                           //  no digits found
 
1821
   {
 
1822
      if (tnb) return 4;                                                   //  non-numeric (invalid) string
 
1823
      else return 3;                                                       //  null or blank string
 
1824
   }
 
1825
 
 
1826
   if (sign == -1) inum = -inum;                                           //  negate if - sign
 
1827
 
 
1828
   if (! tnb) return 0;                                                    //  no trailing non-numerics
 
1829
   else return 1;                                                          //  trailing non-numerics
 
1830
 
 
1831
conv_err:
 
1832
   inum = 0;
 
1833
   return 4;
 
1834
}
 
1835
 
 
1836
 
 
1837
int convSI(const char *string, int & inum, int lolim, int hilim, const char **delim)
 
1838
{
 
1839
   int   stat = convSI(string,inum,delim);
 
1840
 
 
1841
   if (stat > 2) return stat;                                              //  invalid or null/blank
 
1842
   if (inum < lolim) return 2;                                             //  return 2 if out of limits
 
1843
   if (inum > hilim) return 2;                                             //  (has precedence over status 1)
 
1844
   return stat;                                                            //  limits OK, return 0 or 1
 
1845
}
 
1846
 
 
1847
 
 
1848
//  Convert string to double.
 
1849
 
 
1850
int convSD(const char *string, double &dnum, const char **delim)
 
1851
{
 
1852
   char        ch;
 
1853
   int         ii, sign = 0, digits = 0, ndec = 0;
 
1854
   int         exp = 0, esign = 0, edigits = 0, tnb = 0;
 
1855
   const char  *pch = string;
 
1856
 
 
1857
   static int  first = 1;
 
1858
   static double  decimals[21], exponents[74];
 
1859
 
 
1860
   if (first)                                                              //  first-time called
 
1861
   {
 
1862
      first = 0;                                                           //  pre-calculate constants
 
1863
      for (ii = 1; ii <= 20; ii++) decimals[ii] = pow(10.0,-ii);
 
1864
      for (ii = -36; ii <= 36; ii++) exponents[ii+37] = pow(10.0,ii);
 
1865
   }
 
1866
 
 
1867
   dnum = 0.0;
 
1868
 
 
1869
   while ((ch = *pch) == ' ') pch++;                                       //  skip leading blanks
 
1870
 
 
1871
   if (ch == '-') sign = -1;                                               //  process leading +/- sign
 
1872
   if (ch == '+') sign = 1;                                                //  (at most one sign character)
 
1873
   if (sign) pch++;
 
1874
 
 
1875
get_digits:
 
1876
 
 
1877
   while ((*pch >= '0') && (*pch <= '9'))                                  //  process digits 0 - 9
 
1878
   {
 
1879
      dnum = 10.0 * dnum + (*pch - '0');
 
1880
      pch++;
 
1881
      digits++;
 
1882
      if (ndec) ndec++;
 
1883
   }
 
1884
 
 
1885
   if ((*pch == '.') || (*pch == ','))                                     //  process decimal point
 
1886
   {                                                                       //  (allow comma or period)
 
1887
      if (ndec) goto conv_err;
 
1888
      ndec++;
 
1889
      pch++;
 
1890
      goto get_digits;
 
1891
   }
 
1892
 
 
1893
   if ((*pch == 'e') || (*pch == 'E'))                                     //  process optional exponent
 
1894
   {
 
1895
      pch++;
 
1896
      if (*pch == '+') esign = 1;                                          //  optional +/- sign
 
1897
      if (*pch == '-') esign = -1;
 
1898
      if (esign) pch++;
 
1899
 
 
1900
      if ((*pch < '0') || (*pch > '9')) goto conv_err;                     //  1st digit
 
1901
      exp = *pch - '0';
 
1902
      edigits++;
 
1903
      pch++;
 
1904
 
 
1905
      if ((*pch >= '0') && (*pch <= '9'))                                  //  optional 2nd digit
 
1906
      {
 
1907
         exp = 10 * exp + (*pch - '0');
 
1908
         edigits++;
 
1909
         pch++;
 
1910
      }
 
1911
 
 
1912
      if ((exp < -36) || (exp > 36)) goto conv_err;                        //  exponent too big 
 
1913
   }
 
1914
 
 
1915
   if (delim) *delim = pch;                                                //  terminating delimiter 
 
1916
   if (*pch && (*pch != ' ')) tnb++;                                       //  not null or blank
 
1917
 
 
1918
   if (!(digits + edigits))                                                //  no digits found
 
1919
   {
 
1920
      if (tnb) return 4;                                                   //  non-numeric (invalid) string
 
1921
      else return 3;                                                       //  null or blank string
 
1922
   }
 
1923
 
 
1924
   if (ndec > 1) dnum = dnum * decimals[ndec-1];                           //  compensate for decimal places
 
1925
 
 
1926
   if (sign == -1) dnum = - dnum;                                          //  negate if negative
 
1927
 
 
1928
   if (exp)                                                
 
1929
   {
 
1930
      if (esign == -1) exp = -exp;                                         //  process exponent
 
1931
      dnum = dnum * exponents[exp+37];
 
1932
   }
 
1933
 
 
1934
   if (! tnb) return 0;                                                    //  no trailing non-numerics
 
1935
   else return 1;                                                          //  trailing non-numerics
 
1936
 
 
1937
conv_err:
 
1938
   dnum = 0.0;
 
1939
   return 4;
 
1940
}
 
1941
 
 
1942
 
 
1943
int convSD(const char *string, double &dnum, double lolim, double hilim, const char **delim)
 
1944
{
 
1945
   int stat = convSD(string,dnum,delim);
 
1946
 
 
1947
   if (stat > 2) return stat;                                              //  invalid or null/blank
 
1948
   if (dnum < lolim) return 2;                                             //  return 2 if out of limits
 
1949
   if (dnum > hilim) return 2;                                             //  (has precedence over status 1)
 
1950
   return stat;                                                            //  limits OK, return 0 or 1
 
1951
}
 
1952
 
 
1953
 
 
1954
//  Convert int to string with returned length.
 
1955
 
 
1956
int convIS(int inum, char *string, int *cc)
 
1957
{
 
1958
   int   ccc;
 
1959
 
 
1960
   ccc = sprintf(string,"%d",inum);
 
1961
   if (cc) *cc = ccc;
 
1962
   return 0;
 
1963
}
 
1964
 
 
1965
 
 
1966
//  Convert double to string with specified digits of precision.
 
1967
//  Shortest length format (f/e) will be used.  
 
1968
//  Output length is returned in optional argument cc.
 
1969
 
 
1970
int convDS(double dnum, int digits, char *string, int *cc)
 
1971
{
 
1972
   char     *pstr;
 
1973
   int      ccc;
 
1974
   
 
1975
   sprintf(string,"%.*g",digits,dnum);
 
1976
 
 
1977
   pstr = strstr(string,"e+");
 
1978
   if (pstr) strcpy(pstr+1,pstr+2);
 
1979
 
 
1980
   pstr = strstr(string,"e0");            
 
1981
   if (pstr) strcpy(pstr+1,pstr+2);
 
1982
 
 
1983
   pstr = strstr(string,"e0");            
 
1984
   if (pstr) strcpy(pstr+1,pstr+2);
 
1985
 
 
1986
   pstr = strstr(string,"e-0");
 
1987
   if (pstr) strcpy(pstr+2,pstr+3);
 
1988
 
 
1989
   pstr = strstr(string,"e-0");
 
1990
   if (pstr) strcpy(pstr+2,pstr+3);
 
1991
 
 
1992
   ccc = strlen(string);
 
1993
   if (cc) *cc = ccc;
 
1994
 
 
1995
   return 0;
 
1996
}
 
1997
 
 
1998
 
 
1999
/**************************************************************************
 
2000
 
 
2001
    Wildcard string match
 
2002
 
 
2003
    Match candidate string to wildcard string containing any number of 
 
2004
    '*' or '?' wildcard characters. '*' matches any number of characters, 
 
2005
    including zero characters. '?' matches any one character.
 
2006
 
 
2007
    Returns 0 if match, 1 if no match.
 
2008
 
 
2009
***************************************************************************/
 
2010
 
 
2011
int MatchWild(const char *pWild, const char *pString)
 
2012
{
 
2013
   int   ii, star;
 
2014
 
 
2015
new_segment:
 
2016
 
 
2017
   star = 0;
 
2018
   while (pWild[0] == '*')
 
2019
   {
 
2020
      star = 1;
 
2021
      pWild++;
 
2022
   }
 
2023
 
 
2024
test_match:
 
2025
 
 
2026
   for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++)
 
2027
   {
 
2028
      if (pWild[ii] != pString[ii])
 
2029
      {
 
2030
         if (! pString[ii]) return 1;
 
2031
         if (pWild[ii] == '?') continue;
 
2032
         if (! star) return 1;
 
2033
         pString++;
 
2034
         goto test_match;
 
2035
      }
 
2036
   }
 
2037
 
 
2038
   if (pWild[ii] == '*')
 
2039
   {
 
2040
      pString += ii;
 
2041
      pWild += ii;
 
2042
      goto new_segment;
 
2043
   }
 
2044
 
 
2045
   if (! pString[ii]) return 0;
 
2046
   if (ii && pWild[ii-1] == '*') return 0;
 
2047
   if (! star) return 1;
 
2048
   pString++;
 
2049
   goto test_match;
 
2050
}
 
2051
 
 
2052
 
 
2053
/**************************************************************************
 
2054
 
 
2055
   SearchWild  - wildcard file search
 
2056
 
 
2057
   Find all files with total /pathname/filename matching a pattern,
 
2058
   which may have any number of the wildcard characters '*' and '?'
 
2059
   in either or both the pathname and filename.
 
2060
 
 
2061
   const char * SearchWild(const char *wfilespec, int &flag)
 
2062
   
 
2063
   inputs:  flag = 1 to start a new search
 
2064
            flag = 2 abort a running search
 
2065
            *** do not modify flag within a search ***
 
2066
 
 
2067
            wfilespec = filespec to search with optional wildcards
 
2068
               e.g. "/name1/na*me2/nam??e3/name4*.ext?"
 
2069
               
 
2070
   return:  a pointer to one matching file is returned per call,
 
2071
            or null when there are no more matching files.
 
2072
             
 
2073
   The search may be aborted before completion, but make a final 
 
2074
   call with flag = 2 to clean up temp file. A new search with 
 
2075
   flag = 1 will also finish the cleanup.
 
2076
   
 
2077
   NOT THREAD SAFE - do not use in parallel threads
 
2078
   
 
2079
   shell find command is used for the initial search because this
 
2080
   is much faster than recursive use of readdir() (why?). 
 
2081
 
 
2082
   (#) is used in place of (*) in comments below to prevent 
 
2083
   compiler from interpreting (#/) as end of comments
 
2084
 
 
2085
   GNU find peculiarities: 
 
2086
     find /path/#      omits "." files
 
2087
     find /path/       includes "." files
 
2088
     find /path/#      recurses directories under /path/
 
2089
     find /path/#.txt  does not recurse directories
 
2090
     find /path/#/     finds all files under /path/
 
2091
     find /path/#/#    finds files >= 1 directory level under /path/
 
2092
     find /path/xxx#   never finds anything
 
2093
 
 
2094
   SearchWild uses simpler and more intuitive matching: 
 
2095
     '/' and '.' are matched by '#'
 
2096
     /path/#.txt finds all .txt files under /path/ at any directory level
 
2097
   
 
2098
***************************************************************************/
 
2099
 
 
2100
const char * SearchWild(const char *wpath, int &uflag)
 
2101
{
 
2102
   static FILE    *fid = 0;
 
2103
   static char    tempfile[60];
 
2104
   static char    matchfile[maxfcc];
 
2105
   char           searchpath[maxfcc];
 
2106
   char           command[maxfcc];
 
2107
   int            cc, err, ignore;
 
2108
   char           *pp;
 
2109
   pid_t          pid;
 
2110
   
 
2111
   if ((uflag == 1) || (uflag == 2)) {                                     //  first call or stop flag
 
2112
      if (fid) {
 
2113
         fclose(fid);                                                      //  if file open, close it
 
2114
         fid = 0;
 
2115
         sprintf(command,"rm -f %s",tempfile);                             //  and delete it
 
2116
         ignore = system(command);
 
2117
      }
 
2118
   }
 
2119
   
 
2120
   if (uflag == 2) return 0;                                               //  kill flag, done
 
2121
      
 
2122
   if (uflag == 1)                                                         //  first call flag
 
2123
   {
 
2124
      cc = strlen(wpath);
 
2125
      if (cc == 0) return 0;
 
2126
      if (cc > maxfcc-20) appcrash("SearchWild: wpath > max");
 
2127
      
 
2128
      pp = (char *) wpath;
 
2129
      repl_Nstrs(pp,searchpath,"$","\\$","\"","\\\"",null);                //  init. search path, escape $ and "
 
2130
 
 
2131
      pp = strchr(searchpath,'*');
 
2132
      if (pp) {                                                            //  not efficient but foolproof 
 
2133
         while ((*pp != '/') && (pp > searchpath)) pp--;                   //  /aaa/bbb/cc*cc... >>> /aaa/bbb/
 
2134
         if (pp > searchpath) *(pp+1) = 0;
 
2135
      }
 
2136
 
 
2137
      pid = getpid();
 
2138
      sprintf(tempfile,"/tmp/searchwild-%u",pid);                          //  unique temp file name
 
2139
      sprintf(command,"find \"%s\" -type f -or -type l > %s",              //  find files (ordinary, symlink)
 
2140
                                 searchpath,tempfile);                     //  output to temp file
 
2141
      err = system(command);                                               //  ignore error
 
2142
      fid = fopen(tempfile,"r");                                           //  open temp file
 
2143
      uflag = 763568954;                                                   //  begin search
 
2144
   }
 
2145
 
 
2146
   if (uflag != 763568954) appcrash("SearchWild, uflag invalid");
 
2147
   
 
2148
   while (true)
 
2149
   {
 
2150
      pp = fgets(matchfile,maxfcc-2,fid);                                  //  next matching file
 
2151
      if (! pp) {
 
2152
         fclose(fid);                                                      //  no more
 
2153
         fid = 0;
 
2154
         sprintf(command,"rm -f %s \n",tempfile);
 
2155
         ignore = system(command);
 
2156
         return 0;
 
2157
      }
 
2158
 
 
2159
      cc = strlen(matchfile);                                              //  get rid of trailing \n
 
2160
      matchfile[cc-1] = 0;
 
2161
 
 
2162
      err = MatchWild(wpath,matchfile);                                    //  wildcard match?
 
2163
      if (err) continue;                                                   //  no
 
2164
 
 
2165
      return matchfile;                                                    //  return file
 
2166
   }
 
2167
}
 
2168
 
 
2169
 
 
2170
/**************************************************************************/
 
2171
 
 
2172
//  perform a binary search on sorted list of integers
 
2173
//  return matching element or -1 if not found
 
2174
 
 
2175
int bsearch(int element, int nn, int list[])
 
2176
{
 
2177
   int      ii, jj, kk, rkk;
 
2178
 
 
2179
   ii = nn / 2;                                                            //  next element to search
 
2180
   jj = (ii + 1) / 2;                                                      //  next increment
 
2181
   nn--;                                                                   //  last element
 
2182
   rkk = 0;
 
2183
 
 
2184
   while (true)
 
2185
   {
 
2186
      kk = list[ii] - element;                                             //  check element
 
2187
 
 
2188
      if (kk > 0)
 
2189
      {
 
2190
         ii -= jj;                                                         //  too high, go down
 
2191
         if (ii < 0) return -1;
 
2192
      }
 
2193
 
 
2194
      else if (kk < 0)
 
2195
      {
 
2196
         ii += jj;                                                         //  too low, go up
 
2197
         if (ii > nn) return -1;
 
2198
      }
 
2199
 
 
2200
      else if (kk == 0) return ii;                                         //  matched
 
2201
 
 
2202
      jj = jj / 2;                                                         //  reduce increment
 
2203
 
 
2204
      if (jj == 0)
 
2205
      {
 
2206
         jj = 1;                                                           //  step by 1 element
 
2207
         if (! rkk) rkk = kk;                                              //  save direction
 
2208
         else
 
2209
         {
 
2210
            if (rkk > 0) { if (kk < 0) return -1; }                        //  if change direction, fail
 
2211
            else if (kk > 0) return -1;
 
2212
         }
 
2213
      }
 
2214
   }
 
2215
}
 
2216
 
 
2217
 
 
2218
/**************************************************************************
 
2219
   heap sort functions
 
2220
***************************************************************************/
 
2221
 
 
2222
#define SWAP(x,y) (temp = (x), (x) = (y), (y) = temp)
 
2223
 
 
2224
 
 
2225
//  heapsort for array of integers 
 
2226
 
 
2227
static void adjust(int vv[], int n1, int n2)
 
2228
{
 
2229
   int   *bb, jj, kk, temp;
 
2230
 
 
2231
   bb = vv - 1;
 
2232
   jj = n1;
 
2233
   kk = n1 * 2;
 
2234
 
 
2235
   while (kk <= n2)
 
2236
   {
 
2237
      if (kk < n2 && bb[kk] < bb[kk+1]) kk++;
 
2238
      if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]);
 
2239
      jj = kk;
 
2240
      kk *= 2;
 
2241
   }
 
2242
}
 
2243
 
 
2244
void HeapSort(int vv[], int nn)
 
2245
{
 
2246
   int   *bb, jj, temp;
 
2247
 
 
2248
   for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn);
 
2249
 
 
2250
   bb = vv - 1;
 
2251
 
 
2252
   for (jj = nn-1; jj > 0; jj--)
 
2253
   {
 
2254
      SWAP(bb[1], bb[jj+1]);
 
2255
      adjust(vv,1,jj);
 
2256
   }
 
2257
}
 
2258
 
 
2259
 
 
2260
//  heapsort for array of floats 
 
2261
 
 
2262
static void adjust(float vv[], int n1, int n2)
 
2263
{
 
2264
   float    *bb, temp;
 
2265
   int      jj, kk;
 
2266
 
 
2267
   bb = vv - 1;
 
2268
   jj = n1;
 
2269
   kk = n1 * 2;
 
2270
 
 
2271
   while (kk <= n2)
 
2272
   {
 
2273
      if (kk < n2 && bb[kk] < bb[kk+1]) kk++;
 
2274
      if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]);
 
2275
      jj = kk;
 
2276
      kk *= 2;
 
2277
   }
 
2278
}
 
2279
 
 
2280
void HeapSort(float vv[], int nn)
 
2281
{
 
2282
   float    *bb, temp;
 
2283
   int      jj;
 
2284
 
 
2285
   for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn);
 
2286
 
 
2287
   bb = vv - 1;
 
2288
 
 
2289
   for (jj = nn-1; jj > 0; jj--)
 
2290
   {
 
2291
      SWAP(bb[1], bb[jj+1]);
 
2292
      adjust(vv,1,jj);
 
2293
   }
 
2294
}
 
2295
 
 
2296
 
 
2297
//  heapsort for array of doubles 
 
2298
 
 
2299
static void adjust(double vv[], int n1, int n2)
 
2300
{
 
2301
   double   *bb, temp;
 
2302
   int      jj, kk;
 
2303
 
 
2304
   bb = vv - 1;
 
2305
   jj = n1;
 
2306
   kk = n1 * 2;
 
2307
 
 
2308
   while (kk <= n2)
 
2309
   {
 
2310
      if (kk < n2 && bb[kk] < bb[kk+1]) kk++;
 
2311
      if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]);
 
2312
      jj = kk;
 
2313
      kk *= 2;
 
2314
   }
 
2315
}
 
2316
 
 
2317
void HeapSort(double vv[], int nn)
 
2318
{
 
2319
   double   *bb, temp;
 
2320
   int      jj;
 
2321
 
 
2322
   for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn);
 
2323
 
 
2324
   bb = vv - 1;
 
2325
 
 
2326
   for (jj = nn-1; jj > 0; jj--)
 
2327
   {
 
2328
      SWAP(bb[1], bb[jj+1]);
 
2329
      adjust(vv,1,jj);
 
2330
   }
 
2331
}
 
2332
 
 
2333
 
 
2334
//  heapsort array of pointers to strings in ascending order of strings
 
2335
//  pointers are sorted, strings are not changed.
 
2336
 
 
2337
static void adjust(char *vv[], int n1, int n2)
 
2338
{
 
2339
   char     **bb, *temp;
 
2340
   int      jj, kk;
 
2341
 
 
2342
   bb = vv - 1;
 
2343
   jj = n1;
 
2344
   kk = n1 * 2;
 
2345
 
 
2346
   while (kk <= n2)
 
2347
   {
 
2348
      if (kk < n2 && strcmp(bb[kk],bb[kk+1]) < 0) kk++;
 
2349
      if (strcmp(bb[jj],bb[kk]) < 0) SWAP(bb[jj],bb[kk]);
 
2350
      jj = kk;
 
2351
      kk *= 2;
 
2352
   }
 
2353
}
 
2354
 
 
2355
void HeapSort(char *vv[], int nn)
 
2356
{
 
2357
   char     **bb, *temp;
 
2358
   int      jj;
 
2359
 
 
2360
   for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn);
 
2361
 
 
2362
   bb = vv;
 
2363
 
 
2364
   for (jj = nn-1; jj > 0; jj--)
 
2365
   {
 
2366
      SWAP(bb[0], bb[jj]);
 
2367
      adjust(vv,1,jj);
 
2368
   }
 
2369
}
 
2370
 
 
2371
 
 
2372
//  heapsort array of pointers to strings in user-defined order.
 
2373
//  pointers are sorted, strings are not changed.
 
2374
 
 
2375
static void adjust(char *vv[], int n1, int n2, HeapSortUcomp fcomp)
 
2376
{
 
2377
   char     **bb, *temp;
 
2378
   int      jj, kk;
 
2379
 
 
2380
   bb = vv - 1;
 
2381
   jj = n1;
 
2382
   kk = n1 * 2;
 
2383
 
 
2384
   while (kk <= n2)
 
2385
   {
 
2386
      if (kk < n2 && fcomp(bb[kk],bb[kk+1]) < 0) kk++;
 
2387
      if (fcomp(bb[jj],bb[kk]) < 0) SWAP(bb[jj],bb[kk]);
 
2388
      jj = kk;
 
2389
      kk *= 2;
 
2390
   }
 
2391
}
 
2392
 
 
2393
void HeapSort(char *vv[], int nn, HeapSortUcomp fcomp)
 
2394
{
 
2395
   char     **bb, *temp;
 
2396
   int      jj;
 
2397
 
 
2398
   for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn,fcomp);
 
2399
 
 
2400
   bb = vv;
 
2401
 
 
2402
   for (jj = nn-1; jj > 0; jj--)
 
2403
   {
 
2404
      SWAP(bb[0], bb[jj]);
 
2405
      adjust(vv,1,jj,fcomp);
 
2406
   }
 
2407
}
 
2408
 
 
2409
 
 
2410
//  heapsort for array of strings or records, 
 
2411
//  using caller-supplied record compare function.
 
2412
//  HeapSortUcomp returns [ -1 0 +1 ]  for  rec1 [ < = > ] rec2
 
2413
//  method: build array of pointers and sort these, then
 
2414
//  use this sorted array to re-order the records at the end.
 
2415
 
 
2416
static int     *vv1, *vv2;
 
2417
 
 
2418
static void adjust(char *recs, int RL, int n1, int n2, HeapSortUcomp fcomp)
 
2419
{
 
2420
   int      *bb, jj, kk, temp;
 
2421
   char     *rec1, *rec2;
 
2422
 
 
2423
   bb = vv1 - 1;
 
2424
   jj = n1;
 
2425
   kk = n1 * 2;
 
2426
 
 
2427
   while (kk <= n2)
 
2428
   {
 
2429
      rec1 = recs + RL * bb[kk];
 
2430
      rec2 = recs + RL * bb[kk+1];
 
2431
      if (kk < n2 && fcomp(rec1,rec2) < 0) kk++;
 
2432
      rec1 = recs + RL * bb[jj];
 
2433
      rec2 = recs + RL * bb[kk];
 
2434
      if (fcomp(rec1,rec2) < 0) SWAP(bb[jj],bb[kk]);
 
2435
      jj = kk;
 
2436
      kk *= 2;
 
2437
   }
 
2438
}
 
2439
 
 
2440
void HeapSort(char *recs, int RL, int NR, HeapSortUcomp fcomp)
 
2441
{
 
2442
   int      *bb, jj, kk, temp, flag;
 
2443
   char     *vvrec;
 
2444
 
 
2445
   vv1 = new int[NR];
 
2446
   for (jj = 0; jj < NR; jj++) vv1[jj] = jj;
 
2447
 
 
2448
   for (jj = NR/2; jj > 0; jj--) adjust(recs,RL,jj,NR,fcomp);
 
2449
 
 
2450
   bb = vv1 - 1;
 
2451
 
 
2452
   for (jj = NR-1; jj > 0; jj--)
 
2453
   {
 
2454
      SWAP(bb[1], bb[jj+1]);
 
2455
      adjust(recs,RL,1,jj,fcomp);
 
2456
   }
 
2457
 
 
2458
   vv2 = new int[NR];
 
2459
   for (jj = 0; jj < NR; jj++) vv2[vv1[jj]] = jj;
 
2460
 
 
2461
   vvrec = new char[RL];
 
2462
   flag = 1;
 
2463
   while (flag)
 
2464
   {
 
2465
      flag = 0;
 
2466
      for (jj = 0; jj < NR; jj++)
 
2467
      {
 
2468
         kk = vv2[jj];
 
2469
         if (kk == jj) continue;
 
2470
         memmove(vvrec,recs+jj*RL,RL);
 
2471
         memmove(recs+jj*RL,recs+kk*RL,RL);
 
2472
         memmove(recs+kk*RL,vvrec,RL);
 
2473
         SWAP(vv2[jj],vv2[kk]);
 
2474
         flag = 1;
 
2475
      }
 
2476
   }
 
2477
 
 
2478
   delete vv1;
 
2479
   delete vv2;
 
2480
   delete vvrec;
 
2481
}
 
2482
 
 
2483
 
 
2484
/**************************************************************************
 
2485
 
 
2486
         int MemSort (char *RECS, int RL, int NR, int KEYS[][3], int NK)
 
2487
 
 
2488
         RECS is an array of records, to be sorted in-place.
 
2489
         (record length = RL, record count = NR)
 
2490
 
 
2491
         KEYS[NK,3]  is an integer array defined as follows:
 
2492
              [N,0]    starting position of Nth key field in RECS
 
2493
              [N,1]    length of Nth key field in RECS
 
2494
              [N,2]    type of sort for Nth key:
 
2495
                        1 = char ascending
 
2496
                        2 = char descending
 
2497
                        3 = int*4 ascending (int, long)
 
2498
                        4 = int*4 descending
 
2499
                        5 = float*4 ascending (float)
 
2500
                        6 = float*4 descending
 
2501
                        7 = float*8 ascending (double)
 
2502
                        8 = float*8 descending
 
2503
 
 
2504
***************************************************************************/
 
2505
 
 
2506
int MemSortComp(const char *rec1, const char *rec2);
 
2507
int MemSortKeys[10][3], MemSortNK;
 
2508
 
 
2509
int MemSort(char *RECS, int RL, int NR, int KEYS[][3], int NK)
 
2510
{
 
2511
   int   ii;
 
2512
 
 
2513
   if (NR < 2) return 1;
 
2514
 
 
2515
   if (NK > 10) appcrash("MemSort, bad NK");
 
2516
   if (NK < 1) appcrash("MemSort, bad NK");
 
2517
 
 
2518
   MemSortNK = NK;
 
2519
 
 
2520
   for (ii = 0; ii < NK; ii++)
 
2521
   {
 
2522
      MemSortKeys[ii][0] = KEYS[ii][0];
 
2523
      MemSortKeys[ii][1] = KEYS[ii][1];
 
2524
      MemSortKeys[ii][2] = KEYS[ii][2];
 
2525
   }
 
2526
 
 
2527
   HeapSort(RECS,RL,NR,MemSortComp);
 
2528
   return 1;
 
2529
}
 
2530
 
 
2531
int MemSortComp(const char *rec1, const char *rec2)
 
2532
{
 
2533
   int            ii, stat, kpos, ktype, kleng;
 
2534
   int            inum1, inum2;
 
2535
   float          rnum1, rnum2;
 
2536
   double         dnum1, dnum2;
 
2537
   const char     *p1, *p2;
 
2538
 
 
2539
   for (ii = 0; ii < MemSortNK; ii++)                                      //  loop each key
 
2540
   {
 
2541
      kpos = MemSortKeys[ii][0];                                           //  relative position
 
2542
      kleng = MemSortKeys[ii][1];                                          //  length
 
2543
      ktype = MemSortKeys[ii][2];                                          //  type
 
2544
 
 
2545
      p1 = rec1 + kpos;                                                    //  absolute position
 
2546
      p2 = rec2 + kpos;
 
2547
 
 
2548
      switch (ktype)
 
2549
      {
 
2550
         case 1:                                                           //  char ascending
 
2551
            stat = strncmp(p1,p2,kleng);                                   //  compare 2 key values
 
2552
            if (stat) return stat;                                         //  + if rec1 > rec2, - if <
 
2553
            break;                                                         //  2 keys are equal, check next key
 
2554
 
 
2555
         case 2:                                                           //  char descending
 
2556
            stat = strncmp(p1,p2,kleng);
 
2557
            if (stat) return -stat;
 
2558
            break;
 
2559
 
 
2560
         case 3:                                                           //  int ascending
 
2561
            memmove(&inum1,p1,4);
 
2562
            memmove(&inum2,p2,4);
 
2563
            if (inum1 > inum2) return 1;
 
2564
            if (inum1 < inum2) return -1;
 
2565
            break;
 
2566
 
 
2567
         case 4:                                                           //  int descending
 
2568
            memmove(&inum1,p1,4);
 
2569
            memmove(&inum2,p2,4);
 
2570
            if (inum1 > inum2) return -1;
 
2571
            if (inum1 < inum2) return 1;
 
2572
            break;
 
2573
 
 
2574
         case 5:                                                           //  float ascending
 
2575
            memmove(&rnum1,p1,4);
 
2576
            memmove(&rnum2,p2,4);
 
2577
            if (rnum1 > rnum2) return 1;
 
2578
            if (rnum1 < rnum2) return -1;
 
2579
            break;
 
2580
 
 
2581
         case 6:                                                           //  float descending
 
2582
            memmove(&rnum1,p1,4);
 
2583
            memmove(&rnum2,p2,4);
 
2584
            if (rnum1 > rnum2) return -1;
 
2585
            if (rnum1 < rnum2) return 1;
 
2586
            break;
 
2587
 
 
2588
         case 7:                                                           //  double ascending
 
2589
            memmove(&dnum1,p1,8);
 
2590
            memmove(&dnum2,p2,8);
 
2591
            if (dnum1 > dnum2) return 1;
 
2592
            if (dnum1 < dnum2) return -1;
 
2593
            break;
 
2594
 
 
2595
         case 8:                                                           //  double descending
 
2596
            memmove(&dnum1,p1,8);
 
2597
            memmove(&dnum2,p2,8);
 
2598
            if (dnum1 > dnum2) return -1;
 
2599
            if (dnum1 < dnum2) return 1;
 
2600
            break;
 
2601
 
 
2602
         default:                                                          //  key type not 1-8
 
2603
            appcrash("MemSort, bad KEYS sort type");
 
2604
      }
 
2605
   }
 
2606
 
 
2607
   return 0;                                                               //  records match on all keys
 
2608
}
 
2609
 
 
2610
 
 
2611
/**************************************************************************/
 
2612
 
 
2613
//  random number generators with explicit context
 
2614
//  and improved randomness over a small series
 
2615
 
 
2616
int lrandz(int64 *seed)                                                    //  returns 0 to 0x7fffffff
 
2617
{
 
2618
   *seed = *seed ^ (*seed << 17);
 
2619
   *seed = *seed ^ (*seed << 20);
 
2620
   return nrand48((unsigned int16 *) seed);
 
2621
}
 
2622
 
 
2623
double drandz(int64 *seed)                                                 //  returns 0.0 to 0.99999...
 
2624
{
 
2625
   *seed = *seed ^ (*seed << 17);
 
2626
   *seed = *seed ^ (*seed << 20);
 
2627
   return erand48((unsigned int16 *) seed);
 
2628
}
 
2629
 
 
2630
 
 
2631
/**************************************************************************
 
2632
   Quick Math Functions
 
2633
 
 
2634
   These functions duplicate C library standard functions but provide 
 
2635
   faster execution at the expense of precision. The precision is 
 
2636
   generally 5 or 6 digits, but test and use with caution.
 
2637
   
 
2638
   They all build a table of values on first call (1-2 millisecs),
 
2639
   which is used to interpolate values in subsequent calls.
 
2640
 
 
2641
   The argument range is also limited - see comments in each function.
 
2642
   
 
2643
   Following benchmarks done with Intel Core 2 Duo at 2.4 GHz
 
2644
 
 
2645
   double qsine(double)       3.2 times as fast as sin()
 
2646
   double qcosine(double)     3.2 times as fast as cos()
 
2647
   double qarcsine(double)    5.1 times as fast as asin()
 
2648
   double qsqrt(double)       2.1 times as fast as sqrt()
 
2649
 
 
2650
***************************************************************************/
 
2651
 
 
2652
double qsine(double x)                                                     //  sine(x) for x = -pi to +pi
 
2653
{                                                                          //  (seg. fault if out of range)
 
2654
   int            ii;
 
2655
   static int     ftf = 1;
 
2656
   double         v1, s1, s2, ans;
 
2657
   static double  base = 3.15;                                             //  pi + a bit more
 
2658
   static double  K1 = base / 500.0;
 
2659
   static double  K2 = 500.0 / base;
 
2660
   static double  v[1002], sinv[1002];
 
2661
 
 
2662
   if (ftf) {
 
2663
      ftf = 0;
 
2664
      for (ii = 0; ii < 1002; ii++)
 
2665
      {
 
2666
         v1 = ii * K1 - base;                                              //  -pi to +pi (a bit more each end)
 
2667
         v[ii] = v1;
 
2668
         sinv[ii] = sin(v1);
 
2669
      }
 
2670
   }
 
2671
   
 
2672
   ii = int(K2 * (x + base));
 
2673
   v1 = v[ii];
 
2674
   s1 = sinv[ii];
 
2675
   s2 = sinv[ii+1];
 
2676
   ans = s1 + (s2 - s1) * (x - v1) * K2;
 
2677
   return ans;
 
2678
}
 
2679
 
 
2680
double qcosine(double x)                                                   //  cosine(x) for x = -pi to +pi
 
2681
{                                                                          //  (seg. fault if out of range)
 
2682
   int            ii;
 
2683
   static int     ftf = 1;
 
2684
   double         v1, s1, s2, ans;
 
2685
   static double  base = 3.15;                                             //  pi + a bit more
 
2686
   static double  K1 = base / 500.0;
 
2687
   static double  K2 = 500.0 / base;
 
2688
   static double  v[1002], cosinv[1002];
 
2689
 
 
2690
   if (ftf) {
 
2691
      ftf = 0;
 
2692
      for (ii = 0; ii < 1002; ii++)
 
2693
      {
 
2694
         v1 = ii * K1 - base;                                              //  -pi to +pi (a bit more each end)
 
2695
         v[ii] = v1;
 
2696
         cosinv[ii] = cos(v1);
 
2697
      }
 
2698
   }
 
2699
   
 
2700
   ii = int(K2 * (x + base));
 
2701
   v1 = v[ii];
 
2702
   s1 = cosinv[ii];
 
2703
   s2 = cosinv[ii+1];
 
2704
   ans = s1 + (s2 - s1) * (x - v1) * K2;
 
2705
   return ans;
 
2706
}
 
2707
 
 
2708
double qarcsine(double x)                                                  //  arcsine(x) for x = -1 to +1
 
2709
{                                                                          //  (seg. fault if out of range)
 
2710
   int            ii;
 
2711
   static int     ftf = 1;
 
2712
   double         v1, s1, s2, ans;
 
2713
   static double  base = 1.0;
 
2714
   static double  K1 = base / 1000.0;
 
2715
   static double  K2 = 1000.0 / base;
 
2716
   static double  v[2001], arcsinv[2001];
 
2717
 
 
2718
   if (ftf) {
 
2719
      ftf = 0;
 
2720
      for (ii = 0; ii < 2001; ii++)
 
2721
      {
 
2722
         v1 = ii * K1 - base;                                              //  -1 to +1, exactly
 
2723
         v[ii] = v1;
 
2724
         arcsinv[ii] = asin(v1);
 
2725
      }
 
2726
   }
 
2727
 
 
2728
   ii = int(K2 * (x + base));
 
2729
   if (ii < 50 || ii > 1950) return asin(x);                               //  improve accuracy near 90 deg.
 
2730
   v1 = v[ii];
 
2731
   s1 = arcsinv[ii];
 
2732
   s2 = arcsinv[ii+1];
 
2733
   ans = s1 + (s2 - s1) * (x - v1) * K2;
 
2734
   return ans;
 
2735
}
 
2736
 
 
2737
double qsqrt(double x)                                                     //  sqrt(x) for x = 0 to 1
 
2738
{                                                                          //  (seg. fault if out of range)
 
2739
   int               ii;
 
2740
   static int        ftf = 1;
 
2741
   static double     *sqrtv;
 
2742
 
 
2743
   if (ftf) {
 
2744
      ftf = 0;
 
2745
      sqrtv = (double *) zmalloc(100001 * sizeof(double));
 
2746
      for (ii = 0; ii < 100001; ii++)
 
2747
            sqrtv[ii] = sqrt(0.00001 * ii + 0.000005);
 
2748
   }
 
2749
 
 
2750
   ii = int(100000.0 * x);
 
2751
   if (ii < 100) return sqrt(x);                                           //  improve accuracy near zero
 
2752
 
 
2753
   return sqrtv[ii];                                                       //  interpolation slower than sqrt()
 
2754
}                                                                          //  (done in cpu microcode)
 
2755
 
 
2756
 
 
2757
/**************************************************************************
 
2758
 
 
2759
   spline1: define a curve using a set of data points (x and y values)        v.2.11
 
2760
   spline2: for a given x-value, return a y-value fitting the curve
 
2761
 
 
2762
   For spline1, the no. of curve-defining points must be < 100.
 
2763
   For spline2, the given x-value must be within the range defined in spline1.
 
2764
 
 
2765
***************************************************************************/
 
2766
 
 
2767
namespace splinedata 
 
2768
{
 
2769
   int      nn;
 
2770
   double   px1[100], py1[100], py2[100];
 
2771
}
 
2772
 
 
2773
using namespace splinedata;
 
2774
 
 
2775
 
 
2776
void spline1(int dnn, double *dx1, double *dy1)
 
2777
{
 
2778
   double   sig, p, u[100];
 
2779
   int      ii;
 
2780
   
 
2781
   nn = dnn;
 
2782
   if (nn > 100) zappcrash("spline1(), > 100 data points");
 
2783
 
 
2784
   for (ii = 0; ii < nn; ii++)
 
2785
   {
 
2786
      px1[ii] = dx1[ii];
 
2787
      py1[ii] = dy1[ii];
 
2788
      if (ii && px1[ii] <= px1[ii-1]) 
 
2789
         zappcrash("spline1(), x-value not increasing");
 
2790
   }
 
2791
 
 
2792
   py2[0] = u[0] = 0;
 
2793
   
 
2794
   for (ii = 1; ii < nn-1; ii++)
 
2795
   {
 
2796
      sig = (px1[ii] - px1[ii-1]) / (px1[ii+1] - px1[ii-1]);
 
2797
      p = sig * py2[ii-1] + 2;
 
2798
      py2[ii] = (sig - 1) / p;
 
2799
      u[ii] = (6 * ((py1[ii+1] - py1[ii]) / (px1[ii+1] - px1[ii]) - (py1[ii] - py1[ii-1])
 
2800
            / (px1[ii] - px1[ii-1])) / (px1[ii+1] - px1[ii-1]) - sig * u[ii-1]) / p;
 
2801
   }
 
2802
   
 
2803
   py2[nn-1] = 0;
 
2804
   
 
2805
   for (ii = nn-2; ii >= 0; ii--)
 
2806
      py2[ii] = py2[ii] * py2[ii+1] + u[ii];
 
2807
 
 
2808
   return;
 
2809
}
 
2810
 
 
2811
 
 
2812
double spline2(double x)
 
2813
{
 
2814
   int      kk, klo = 0, khi = nn-1;
 
2815
   double   h, a, b, y;
 
2816
   
 
2817
   while (khi - klo > 1)
 
2818
   {
 
2819
      kk = (khi + klo) / 2;
 
2820
      if (px1[kk] > x) khi = kk;
 
2821
      else klo = kk;
 
2822
   }
 
2823
   
 
2824
   h = px1[khi] - px1[klo];
 
2825
   a = (px1[khi] - x) / h;
 
2826
   b = (x - px1[klo]) / h;
 
2827
   y = a * py1[klo] + b * py1[khi] + ((a*a*a - a) * py2[klo] 
 
2828
                                   + (b*b*b - b) * py2[khi]) * (h*h) / 6;
 
2829
   
 
2830
   return y;
 
2831
}
 
2832
 
 
2833
using namespace std;
 
2834
 
 
2835
 
 
2836
/**************************************************************************
 
2837
   Initialize application files according to following conventions:
 
2838
     + use application directories defined by Makefile 
 
2839
     + user data files are in /home/user/.appname/
 
2840
     + if not already attached to a terminal, redirect stdout/stderr 
 
2841
       to log file at /home/user/.appname/appname.log
 
2842
   
 
2843
   zappname    application name:          dcopp, fotoxx, etc.
 
2844
   zdatadir    installed data files       translations, parameters, etc.
 
2845
   zdocdir     user documentation         userguide, README, CHANGES
 
2846
   zicondir    icons                      icon files .png
 
2847
   zuserdir    /home/user/.appname/       log file, user parameters
 
2848
***************************************************************************/
 
2849
 
 
2850
char     zappname[20];
 
2851
char     zdatadir[200], zdocdir[200], zicondir[200], zuserdir[200];
 
2852
char     zlanguage[8] = "en";                                              //  "lc" or "lc_RC"   v.2.14
 
2853
 
 
2854
const char * get_zuserdir() { return  zuserdir; }                          //  /home/user/.appname
 
2855
const char * get_zdatadir() { return  zdatadir; }                          //  parameters, icons
 
2856
 
 
2857
int initz_appfiles(const char *appname, ...)
 
2858
{
 
2859
   char           work[200], *pp;
 
2860
   const char     *appfile;
 
2861
   int            err;
 
2862
   FILE           *fid;
 
2863
   struct stat    statdat;
 
2864
   va_list        arglist;
 
2865
   
 
2866
   catch_signals();                                                        //  catch segfault, backtrace   v.2.22
 
2867
   
 
2868
   strcpy(zappname,appname);
 
2869
 
 
2870
   strcpy(zdatadir,DATADIR);                                               //  macros from build script
 
2871
   strcpy(zdocdir,DOCDIR);
 
2872
   strcpy(zicondir,zdatadir);
 
2873
   strcat(zicondir,"/icons");
 
2874
 
 
2875
   pp = cuserid(0);
 
2876
   if (strEqu(pp,"root")) snprintf(zuserdir,199,"/root/.%s",zappname);     //  get /root/.appname
 
2877
   else snprintf(zuserdir,199,"%s/.%s",getenv("HOME"),zappname);           //  or /home/user/.appname
 
2878
 
 
2879
   err = stat(zuserdir,&statdat);                                          //  does it exist already?
 
2880
   if (err) {
 
2881
      snprintf(work,199,"mkdir -m 0700 %s",zuserdir);                      //  no, create it
 
2882
      err = system(work);
 
2883
      if (err) zappcrash("%s, %s \n",wstrerror(err),work);                 //  cannot
 
2884
 
 
2885
      va_start(arglist,appname);                                           //  copy req. application files
 
2886
      while (true) {                                                       //   from install directory to 
 
2887
         appfile = va_arg(arglist, const char *);                          //    to /home/user/.appname/
 
2888
         if (! appfile) break;
 
2889
         snprintf(work,199,"cp %s/%s %s",zdatadir,appfile,zuserdir);
 
2890
         err = system(work);
 
2891
         if (err) printf("%s, %s \n",wstrerror(err),work);                 //  GTK not started yet
 
2892
      }
 
2893
      va_end(arglist);
 
2894
   }
 
2895
 
 
2896
   if (! isatty(1)) { 
 
2897
      snprintf(work,199,"%s/%s.log",zuserdir,zappname);                    //  if not attached to a terminal,
 
2898
      fid = freopen(work,"w",stdout);                                      //   redirect output to log file
 
2899
      fid = freopen(work,"w",stderr);                                      //    /home/user/.appname/appname.log
 
2900
      printf("%s message and error log \n",zappname);                      //  append to old file   v.2.5
 
2901
   }
 
2902
 
 
2903
   return 1;
 
2904
}
 
2905
 
 
2906
 
 
2907
//  display help file in a separate process so application is not blocked
 
2908
 
 
2909
void showz_helpfile()
 
2910
{
 
2911
   int      err;
 
2912
   char     docfile[200], lang[4];
 
2913
 
 
2914
   snprintf(docfile,199,"%s/userguide-%s.html",zdocdir,zlanguage);         //  .../userguide-lc_RC.html  v.2.14
 
2915
   err = access(docfile,R_OK);
 
2916
   if (err) {
 
2917
      strncpy0(lang,zlanguage,3);
 
2918
      snprintf(docfile,199,"%s/userguide-%s.html",zdocdir,lang);           //  .../userguide-lc.html
 
2919
      err = access(docfile,R_OK);
 
2920
   }
 
2921
 
 
2922
   if (err) {
 
2923
      snprintf(docfile,199,"%s/userguide-en.html",zdocdir);                //  .../userguide-en.html
 
2924
      err = access(docfile,R_OK);
 
2925
   }
 
2926
 
 
2927
   if (err) {
 
2928
      zmessageACK(ZTX("help file not found: %s"),docfile);
 
2929
      return;
 
2930
   }
 
2931
 
 
2932
   showz_html(docfile);                                                    //  v.2.18
 
2933
   return;
 
2934
}
 
2935
 
 
2936
 
 
2937
//  display various admin text files in popup window
 
2938
 
 
2939
void showz_readme()
 
2940
{
 
2941
   char     command[200];
 
2942
   snprintf(command,199,"cat %s/README",zdocdir);
 
2943
   popup_command(command,500,300);
 
2944
   return;
 
2945
}
 
2946
 
 
2947
void showz_changelog()                                                     //  1st 50 lines
 
2948
{
 
2949
   char     command[200];
 
2950
   snprintf(command,199,"head -n 50 %s/CHANGES",zdocdir);
 
2951
   popup_command(command,500,300);
 
2952
   return;
 
2953
}
 
2954
 
 
2955
void showz_translations()
 
2956
{
 
2957
   char     command[200];
 
2958
   snprintf(command,199,"cat %s/TRANSLATIONS",zdocdir);
 
2959
   popup_command(command,500,300);
 
2960
   return;
 
2961
}
 
2962
 
 
2963
 
 
2964
//  create a desktop icon / launcher with icon                             //  v.2.18
 
2965
//  target system needs to be LSB compliant
 
2966
 
 
2967
void zmake_launcher(const char *categories, const char *genericname)
 
2968
{
 
2969
   char     dtfile[200], work[200];
 
2970
   FILE     *fid;
 
2971
   int      err;
 
2972
   
 
2973
   snprintf(dtfile,199,"%s/Desktop/kornelix-%s.desktop",getenv("HOME"),zappname);
 
2974
   fid = fopen(dtfile,"w");
 
2975
   if (! fid) {
 
2976
      zmessageACK(ZTX("error: %s"),strerror(errno));
 
2977
      return;
 
2978
   }
 
2979
   
 
2980
   fputs("[Desktop Entry]\n",fid);
 
2981
   snprintf(work,199,"Name=%s\n",zappname);
 
2982
   fputs(work,fid);
 
2983
   snprintf(work,199,"Categories=%s\n",categories);
 
2984
   fputs(work,fid);
 
2985
   fputs("Type=Application\n",fid);
 
2986
   fputs("Terminal=false\n",fid);
 
2987
   snprintf(work,199,"Exec=%s/%s\n",BINDIR,zappname);
 
2988
   fputs(work,fid);
 
2989
   snprintf(work,199,"Icon=%s/icons/%s.png\n",DATADIR,zappname);
 
2990
   fputs(work,fid);
 
2991
   snprintf(work,199,"GenericName=%s\n",genericname);
 
2992
   fputs(work,fid);
 
2993
   fclose(fid);
 
2994
 
 
2995
   snprintf(work,199,"xdg-desktop-menu install %s",dtfile);
 
2996
   err = system(work);
 
2997
   if (err) zmessLogACK("error: %s",wstrerror(err));
 
2998
   return;
 
2999
}
 
3000
 
 
3001
 
 
3002
/**************************************************************************
 
3003
 
 
3004
   Translation Functions               v.2.9 revised to use .po files
 
3005
   
 
3006
   Translation files are standard .po files as used in the Gnu gettext
 
3007
   system. However the .po files are used directly, and there is no need
 
3008
   to merge and compile them into a binary format (.mo files).
 
3009
 
 
3010
   Initialize: int ZTXinit(const char *lang)
 
3011
      lang is "lc" or "lc_RC" or current locale if null.
 
3012
      status: 0 = OK, 1 = cannot process .po translation file(s).
 
3013
 
 
3014
   If files are found in both  .../lc/xxx.po  and  .../lc_RC/xxx.po,
 
3015
   both sets of files are processed. If a translation is present in
 
3016
   both sets, the regional dialect (lc_RC) will be used.
 
3017
 
 
3018
   Translate a text string: const char * ZTX(const char *english)
 
3019
      english: text string to translate, possibly containing 
 
3020
      printf formats (%d %s ...) and escaped characters (\" \n ...).
 
3021
 
 
3022
   If the user language is English or if no translation is found, 
 
3023
   the input string is returned, else the translated string.
 
3024
   
 
3025
   A text string may have a context part "context::string", where
 
3026
   "context" is any string < 30 characters and "string" is the 
 
3027
   English text or the translation text. The context part "context::" 
 
3028
   is removed in the returned string. This is to handle the case where 
 
3029
   a single English string may need multiple translations, depending 
 
3030
   on context. The English string may be present multiple times in a 
 
3031
   .po file, each one marked with a different context and having a 
 
3032
   different translation. Context is optional in translation strings.
 
3033
   
 
3034
   example: 
 
3035
      
 
3036
      program code: 
 
3037
 
 
3038
         printf(ZTX("answer: %d %s \n more on next line"), 123, "qwerty");
 
3039
      
 
3040
      A German .po file would have the following entry:
 
3041
 
 
3042
         msgid: "answer: %d %s \n"
 
3043
                " more on next line"
 
3044
         msgstr: "Antwort: %d %s \n"
 
3045
                 " mehr in der nƤchsten Zeile"
 
3046
 
 
3047
***************************************************************************/
 
3048
 
 
3049
namespace ZTXnames                                                         //  remove GOFUNC usage becasue of
 
3050
{                                                                          //    GCC optimization errors  v.2.18
 
3051
   FILE     *fid;
 
3052
   char     buff[ZTXmaxcc], *porec, *ppq1, *ppq2, lc_RC[8];
 
3053
   char     *pstring, Estring[ZTXmaxcc], Tstring[ZTXmaxcc];
 
3054
   int      ent, scc;
 
3055
   void     ZTXread_pofiles();
 
3056
   void     ZTXgetstring();
 
3057
   char     **ZTXenglish = null;                                           //  English strings and 
 
3058
   char     **ZTXtrans = null;                                             //    corresp. translations
 
3059
}
 
3060
 
 
3061
using namespace ZTXnames;
 
3062
 
 
3063
int ZTXinit(const char *lang)                                              //  initialize translations
 
3064
{
 
3065
   int      ii;
 
3066
   char     *pp;
 
3067
 
 
3068
   if (ZTXenglish) {
 
3069
      for (ii = 0; ZTXenglish[ii]; ii++) {                                 //  free prior translation
 
3070
         zfree(ZTXenglish[ii]);
 
3071
         if (ZTXtrans[ii]) zfree(ZTXtrans[ii]);
 
3072
      }
 
3073
      zfree(ZTXenglish);
 
3074
      zfree(ZTXtrans);
 
3075
      ZTXenglish = ZTXtrans = null;                                        //  set no translation
 
3076
   }
 
3077
 
 
3078
   if (! blank_null(lang)) strncpy0(zlanguage,lang,6);                     //  use language from caller
 
3079
   else {                                                                  //  help Linux chaos     v.2.9.1
 
3080
      pp = getenv("LANG");                                                 //  use $LANG if defined
 
3081
      if (! pp) pp = getenv("LANGUAGE");                                   //  use $LANGUAGE if defined 
 
3082
      if (! pp) pp = setlocale(LC_MESSAGES,"");                            //  use locale if defined
 
3083
      if (pp) strncpy0(zlanguage,pp,6);                                    //  "lc_RC" lang/region codes  v.2.14
 
3084
      else strcpy(zlanguage,"en");                                         //  use English
 
3085
   }
 
3086
 
 
3087
   if (*zlanguage < 'a') strcpy(zlanguage,"en");                           //  use English if garbage
 
3088
   printf("language: %s \n",zlanguage);
 
3089
 
 
3090
   if (strEqu(zlanguage,"en")) return 0;                                   //  English, do nothing
 
3091
 
 
3092
   ZTXenglish = (char **) zmalloc(ZTXmaxent * sizeof(char *));             //  allocate slots for english text
 
3093
   ZTXtrans = (char **) zmalloc(ZTXmaxent * sizeof(char *));               //    strings and translations
 
3094
   ent = 0;
 
3095
 
 
3096
   strncpy0(lc_RC,zlanguage,6);                                            //  process .../locales/lc_RC/*.po
 
3097
   if (strlen(lc_RC) > 3 && strNeq(lc_RC,"en_US")) 
 
3098
      ZTXread_pofiles();
 
3099
 
 
3100
   strncpy0(lc_RC,zlanguage,3);                                            //  process .../locales/lc/*.po
 
3101
   if (strNeq(lc_RC,"en")) ZTXread_pofiles();
 
3102
 
 
3103
   ZTXenglish[ent] = ZTXtrans[ent] = null;                                 //  mark both EOLs
 
3104
   return 0;
 
3105
}
 
3106
 
 
3107
void ZTXnames::ZTXread_pofiles()
 
3108
{
 
3109
   int      ii, contx = 0;
 
3110
   char     *pofile, command[200];
 
3111
 
 
3112
   snprintf(command,199,"find %s/locales/%s/*.po 2>/dev/null",zdatadir,lc_RC);
 
3113
 
 
3114
   while ((pofile = command_output(contx,command)))
 
3115
   {
 
3116
      fid = fopen(pofile,"r");                                             //  open .po file
 
3117
      if (! fid) {
 
3118
         printf("cannot open translation file: %s \n",pofile);             //  error, ignore file
 
3119
         zfree(pofile);
 
3120
         continue;
 
3121
      }
 
3122
      
 
3123
      porec = 0;                                                           //  no .po record yet
 
3124
      *Estring = *Tstring = 0;                                             //  no strings yet
 
3125
 
 
3126
      while (true)
 
3127
      {
 
3128
         if (! porec) porec = fgets_trim(buff,ZTXmaxcc,fid);               //  get next .po record
 
3129
         if (! porec) break;                                               //  EOF
 
3130
         
 
3131
         if (blank_null(porec)) {                                          //  blank record
 
3132
            porec = 0;
 
3133
            continue;
 
3134
         }
 
3135
         if (*porec == '#') {                                              //  comment
 
3136
            porec = 0;
 
3137
            continue;
 
3138
         }
 
3139
 
 
3140
         if (strnEqu(porec,"msgid",5))                                     //  start new english string
 
3141
         {
 
3142
            if (*Estring) {                                                //  two in a row
 
3143
               printf(" no translation: %s \n",Estring);
 
3144
               *Estring = 0;
 
3145
            }
 
3146
 
 
3147
            if (*Tstring) {                                                //  should not happen
 
3148
               printf(" orphan translation: %s \n",Tstring);
 
3149
               *Tstring = 0;
 
3150
            }
 
3151
 
 
3152
            porec += 5;                                                    //  parse string (multiple pieces)
 
3153
            pstring = Estring;
 
3154
            scc = 0;
 
3155
            ZTXgetstring();
 
3156
 
 
3157
            if (*Estring) {
 
3158
               strcpy(Tstring,Estring);                                    //  borrow Tstring
 
3159
               clean_escapes(Tstring);
 
3160
               for (ii = 0; ZTXenglish[ii]; ii++)                          //  find existing entry, if any
 
3161
                  if (strEqu(Tstring,ZTXenglish[ii])) break;
 
3162
               if (ii < ent) {
 
3163
                  {  /*** duplicate english ***/  }
 
3164
                  *Estring = 0;
 
3165
               }
 
3166
               *Tstring = 0;
 
3167
            }
 
3168
            else  {  /*** empty english string ***/  }
 
3169
         }
 
3170
 
 
3171
         else if (strnEqu(porec,"msgstr",6))                               //  start new translation
 
3172
         {
 
3173
            porec += 6;                                                    //  parse the string
 
3174
            pstring = Tstring;
 
3175
            scc = 0;
 
3176
            ZTXgetstring();
 
3177
            
 
3178
            if (! *Tstring) {
 
3179
               if (*Estring) printf(" no translation: %s \n",Estring);
 
3180
               *Estring = *Tstring = 0;
 
3181
            }
 
3182
            else if (! *Estring) {
 
3183
               {  /*** orphan translation ***/  }
 
3184
               *Estring = *Tstring = 0;
 
3185
            }
 
3186
         }
 
3187
         
 
3188
         else
 
3189
         {
 
3190
            printf(" unrecognized input record: %s \n",porec);
 
3191
            porec = 0;
 
3192
            continue;
 
3193
         }
 
3194
         
 
3195
         if (*Estring && *Tstring)
 
3196
         {
 
3197
            if (ent == ZTXmaxent)
 
3198
               zappcrash("more than %d translations",ZTXmaxent);
 
3199
            clean_escapes(Estring);
 
3200
            clean_escapes(Tstring);
 
3201
            ZTXenglish[ent] = strdupz(Estring);                            //  add new English string
 
3202
            ZTXtrans[ent] = strdupz(Tstring);                              //    and translation
 
3203
            ent++;
 
3204
            *Estring = *Tstring = 0;
 
3205
         }
 
3206
      }
 
3207
 
 
3208
      fclose(fid);
 
3209
      zfree(pofile);
 
3210
   }
 
3211
   
 
3212
   return;
 
3213
}
 
3214
 
 
3215
void ZTXnames::ZTXgetstring()
 
3216
{
 
3217
   int      cc;
 
3218
 
 
3219
   while (true)                                                            //  join multiple quoted strings
 
3220
   {
 
3221
      while (*porec && *porec != '"') porec++;                             //  find opening string quote
 
3222
      {
 
3223
         if (! *porec) {
 
3224
            porec = fgets_trim(buff,ZTXmaxcc,fid);                         //  get next .po record
 
3225
            if (! porec) return;
 
3226
            if (strnEqu(porec,"msgid",5)) return;
 
3227
            if (strnEqu(porec,"msgstr",6)) return;
 
3228
         }
 
3229
      }
 
3230
      ppq1 = porec;
 
3231
      ppq2 = ppq1 + 1;
 
3232
      while ((*ppq2 && *ppq2 != '"') ||                                    //  find closing (non-escaped) quote
 
3233
             (*ppq2 == '"' && *(ppq2-1) == '\\')) ppq2++;
 
3234
      if (! *ppq2) return;
 
3235
      cc = ppq2 - ppq1 - 1;
 
3236
      if (cc && scc + cc + 1 < ZTXmaxcc) {                                 //  truncate if too long
 
3237
         strncpy0(pstring+scc,ppq1+1,cc+1);                                //  accum. substrings, minus quotes
 
3238
         scc += cc;
 
3239
      }
 
3240
      porec = ppq2 + 1;
 
3241
   }
 
3242
   
 
3243
   return;
 
3244
}
 
3245
 
 
3246
using namespace std;
 
3247
 
 
3248
 
 
3249
//  Translate the input english string or return the input string.
 
3250
//  Look for "context::string" and return "string" only if context found.
 
3251
//
 
3252
//  This function is not efficient: may need a few microseconds.
 
3253
//  This can be improved if needed by sorting the english strings and 
 
3254
//  using a binary search (1000 strings >> 10 compares instead of 1-1000).
 
3255
 
 
3256
const char * ZTX(const char *english)
 
3257
{
 
3258
   const char  *pp, *pp2;
 
3259
   
 
3260
   if (! english) return 0;
 
3261
 
 
3262
   pp = 0;
 
3263
 
 
3264
   if (ZTXtrans) 
 
3265
   {
 
3266
      for (int ii = 0; ZTXenglish[ii]; ii++)                               //  find translation
 
3267
      {
 
3268
         if (strEqu(english,ZTXenglish[ii])) {
 
3269
            pp = ZTXtrans[ii];
 
3270
            break;
 
3271
         }
 
3272
      }
 
3273
   }
 
3274
   
 
3275
   if (! pp) pp = english;
 
3276
   
 
3277
   for (pp2 = pp; *pp2 && pp2 < pp+30; pp2++)                              //  remove context if present  v.2.11
 
3278
      if (*pp2 == ':' && *(pp2+1) == ':') return pp2+2;
 
3279
 
 
3280
   return pp;
 
3281
}
 
3282
 
 
3283
 
 
3284
/**************************************************************************
 
3285
   GTK windowing system utility functions
 
3286
***************************************************************************/
 
3287
 
 
3288
//  functions to lock GTK calls from threads only
 
3289
//    - do nothing if called from main thread (avoid fatal bug)
 
3290
//    - detect nested calls and make inoccuous (avoid fatal bug)
 
3291
 
 
3292
#define     tmax 20                                                        //  max. simultaneous GTK threads
 
3293
pthread_t   tid_main = 0;
 
3294
pthread_t   tids[tmax];
 
3295
int         tlocks[tmax];
 
3296
int         zinit = 0;
 
3297
mutex       zmutex;
 
3298
 
 
3299
void zlockInit()                                                           //  initz. call from main()
 
3300
{
 
3301
   tid_main = pthread_self();
 
3302
   mutex_init(&zmutex,null);
 
3303
   zinit++;
 
3304
 
 
3305
   for (int ii = 0; ii < tmax; ii++) {
 
3306
      tids[ii] = 0; 
 
3307
      tlocks[ii] = 0;
 
3308
   }
 
3309
   return;
 
3310
}
 
3311
 
 
3312
void zlock()                                                               //  lock GTK if in a thread
 
3313
{
 
3314
   int         ii;
 
3315
   pthread_t   tid_me;
 
3316
 
 
3317
   if (! zinit) zappcrash("zlock(): zinit() not done");
 
3318
 
 
3319
   tid_me = pthread_self();
 
3320
   if (pthread_equal(tid_main,tid_me)) return;                             //  main() thread, do nothing
 
3321
 
 
3322
   mutex_lock(&zmutex);
 
3323
 
 
3324
   for (ii = 0; ii < tmax; ii++)                                           //  find my thread slot
 
3325
         if (pthread_equal(tids[ii],tid_me)) goto zret;
 
3326
   for (ii = 0; ii < tmax; ii++)                                           //  find a free slot
 
3327
         if (tids[ii] == 0) goto zret;
 
3328
   zappcrash("zlock(): too many threads");
 
3329
 
 
3330
zret:
 
3331
   tids[ii] = tid_me;
 
3332
   ++tlocks[ii];
 
3333
   mutex_unlock(&zmutex);
 
3334
 
 
3335
   if (tlocks[ii] == 1) gdk_threads_enter();                               //  1st lock, lock GTK
 
3336
   return;
 
3337
}
 
3338
 
 
3339
void zunlock()                                                             //  unlock GTK if in a thread
 
3340
{
 
3341
   int         ii;
 
3342
   pthread_t   tid_me;
 
3343
 
 
3344
   tid_me = pthread_self();
 
3345
   if (pthread_equal(tid_main,tid_me)) return;                             //  main() thread, do nothing
 
3346
 
 
3347
   for (ii = 0; ii < tmax; ii++)                                           //  find this thread
 
3348
      if (pthread_equal(tids[ii],tid_me)) break;
 
3349
   if (ii == tmax) zappcrash("zunlock(): not locked");
 
3350
 
 
3351
   --tlocks[ii];                                                           //  decrement locks
 
3352
   if (tlocks[ii] == 0) {
 
3353
      tids[ii] = 0;                                                        //  last lock removed, free slot
 
3354
      gdk_flush();
 
3355
      gdk_threads_leave();                                                 //  unlock GTK
 
3356
   }
 
3357
   return;
 
3358
}
 
3359
 
 
3360
 
 
3361
//  iterate main loop every "skip" calls and only if in main() thread
 
3362
 
 
3363
void zmainloop(int skip)                                                   //  v.2.8
 
3364
{
 
3365
   static int  xskip = 0;
 
3366
   pthread_t   tid_me;
 
3367
   
 
3368
   if (skip) {
 
3369
      if (++xskip < skip) return;
 
3370
      xskip = 0;
 
3371
   }
 
3372
 
 
3373
   if (! zinit) zappcrash("zmainloop(): zinit() not done");
 
3374
   tid_me = pthread_self();
 
3375
   if (! pthread_equal(tid_main,tid_me)) return;
 
3376
 
 
3377
   while (gtk_events_pending()) gtk_main_iteration(); 
 
3378
   return;
 
3379
}
 
3380
 
 
3381
 
 
3382
//  write message to text view window
 
3383
//  line:   +N    existing lines from top (replace)
 
3384
//          -N    existing lines from bottom (replace)
 
3385
//           0    next line (add new line at bottom)
 
3386
//  scroll logic assumes only one \n per message
 
3387
 
 
3388
void wprintx(GtkWidget *mLog, int line, const char *message, const char *font)
 
3389
{
 
3390
   static GtkTextMark      *endMark = 0;
 
3391
   GtkTextBuffer           *textBuff;
 
3392
   GtkTextIter             iter1, iter2;
 
3393
   static GtkTextTag       *fontag, *fontags[20];
 
3394
   static char             *fontmem[20];
 
3395
   static int              nfonts = 0;
 
3396
   int                     ii, nlines, scroll = 0;
 
3397
 
 
3398
   zlock();
 
3399
   
 
3400
   textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog));
 
3401
   
 
3402
   endMark = gtk_text_buffer_get_mark(textBuff,"wpxend");                  //  new buffer?
 
3403
   if (! endMark) {
 
3404
      gtk_text_buffer_get_end_iter(textBuff,&iter1);                       //  yes, set my end mark
 
3405
      endMark = gtk_text_buffer_create_mark(textBuff,"wpxend",&iter1,0);
 
3406
      for (ii = 0; ii < nfonts; ii++) zfree(fontmem[ii]);                  //  free font tags memory
 
3407
      nfonts = 0;
 
3408
   }
 
3409
 
 
3410
   if (font) {
 
3411
      for (ii = 0; ii < nfonts; ii++) 
 
3412
            if (strEqu(font,fontmem[ii])) break;                           //  use existing font tag if poss.
 
3413
      if (ii == nfonts) {
 
3414
         if (nfonts == 20) zappcrash("wprintx: exceed 20 font tags");
 
3415
         fontmem[ii] = strdupz(font);                                      //  create new font tag
 
3416
         fontags[ii] = gtk_text_buffer_create_tag(textBuff,0,"font",font,0);
 
3417
         nfonts++;
 
3418
      }
 
3419
      fontag = fontags[ii];
 
3420
   }
 
3421
   
 
3422
   nlines = gtk_text_buffer_get_line_count(textBuff);                      //  lines now in buffer
 
3423
 
 
3424
   if (line == 0) scroll++;                                                //  auto scroll is on
 
3425
 
 
3426
   if (line < 0) {
 
3427
      line = nlines + line + 1;                                            //  last lines: -1, -2 ...
 
3428
      if (line < 1) line = 1;                                              //  above top, use line 1
 
3429
   }
 
3430
   
 
3431
   if (line > nlines) line = 0;                                            //  below bottom, treat as append
 
3432
 
 
3433
   if (line == 0) gtk_text_buffer_get_end_iter(textBuff,&iter1);           //  append new line
 
3434
   
 
3435
   if (line > 0) {
 
3436
      gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line-1);            //  old line start
 
3437
      if (line < nlines)
 
3438
          gtk_text_buffer_get_iter_at_line(textBuff,&iter2,line);          //  old line end
 
3439
      if (line == nlines)                                                  //    or buffer end
 
3440
          gtk_text_buffer_get_end_iter(textBuff,&iter2);                   
 
3441
      gtk_text_buffer_delete(textBuff,&iter1,&iter2);                      //  delete old line
 
3442
   }
 
3443
                                                                           //  insert new line
 
3444
   if (font)                                                               //    with optional font
 
3445
         gtk_text_buffer_insert_with_tags(textBuff,&iter1,message,-1,fontag,null);
 
3446
   else  gtk_text_buffer_insert(textBuff,&iter1,message,-1);
 
3447
 
 
3448
   if (scroll)                                                             //  scroll line into view
 
3449
      gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(mLog),endMark,0,0,1,1);
 
3450
 
 
3451
   zmainloop();
 
3452
   zunlock();
 
3453
   return;
 
3454
}
 
3455
 
 
3456
void wprintf(GtkWidget *mLog, int line, cchar *mess, ... )                 //  "printf" version
 
3457
{
 
3458
   va_list  arglist;
 
3459
   char     message[1000];
 
3460
   int      cc;
 
3461
 
 
3462
   va_start(arglist,mess);
 
3463
   cc = vsnprintf(message,999,mess,arglist);
 
3464
   va_end(arglist);
 
3465
 
 
3466
   wprintx(mLog,line,message);
 
3467
   return;
 
3468
}
 
3469
 
 
3470
void wprintf(GtkWidget *mLog, cchar *mess, ... )                           //  "printf", scrolling output
 
3471
{
 
3472
   va_list  arglist;
 
3473
   char     message[1000];
 
3474
   int      cc;
 
3475
 
 
3476
   va_start(arglist,mess);
 
3477
   cc = vsnprintf(message,999,mess,arglist);                               //  stop overflow, remove warning
 
3478
   va_end(arglist);
 
3479
 
 
3480
   wprintx(mLog,0,message);
 
3481
   return;
 
3482
}
 
3483
 
 
3484
 
 
3485
//  scroll a text view window to put a given line on screen
 
3486
//  1st line = 1.  for last line use line = 0.
 
3487
 
 
3488
void wscroll(GtkWidget *mLog, int line)                                    //  v.2.3
 
3489
{
 
3490
   GtkTextBuffer  *textbuff;
 
3491
   GtkTextIter    iter;
 
3492
 
 
3493
   zlock();
 
3494
   textbuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog));
 
3495
   if (line <= 0) line = gtk_text_buffer_get_line_count(textbuff);
 
3496
   line = line - 1;
 
3497
   gtk_text_buffer_get_iter_at_line(textbuff,&iter,line);
 
3498
   gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(mLog),&iter,0,0,0,0);
 
3499
   zunlock();
 
3500
   return;
 
3501
}
 
3502
 
 
3503
 
 
3504
//  clear a text view window and get a new buffer (a kind of defrag)
 
3505
 
 
3506
void wclear(GtkWidget *mLog)
 
3507
{
 
3508
   GtkTextBuffer  *buff;
 
3509
 
 
3510
   zlock();
 
3511
   buff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog));
 
3512
   gtk_text_buffer_set_text(buff,"",-1);
 
3513
   zunlock();
 
3514
   return;
 
3515
}
 
3516
 
 
3517
 
 
3518
//  clear a text view window from designated line to end of buffer
 
3519
 
 
3520
void wclear(GtkWidget *mLog, int line)
 
3521
{
 
3522
   GtkTextBuffer           *textBuff;
 
3523
   GtkTextIter             iter1, iter2;
 
3524
 
 
3525
   zlock();
 
3526
 
 
3527
   textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog));
 
3528
   gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line-1);               //  iter at line start
 
3529
   gtk_text_buffer_get_end_iter(textBuff,&iter2);
 
3530
   gtk_text_buffer_delete(textBuff,&iter1,&iter2);                         //  delete existing line
 
3531
 
 
3532
   zmainloop();
 
3533
   zunlock();
 
3534
   return;
 
3535
}
 
3536
 
 
3537
 
 
3538
//  get text records from a text view window, one per call
 
3539
//  removes trailing new line characters ( \n )
 
3540
 
 
3541
char * wscanf(GtkWidget *mLog, int & ftf)
 
3542
{
 
3543
   GtkTextBuffer  *textBuff;
 
3544
   GtkTextIter    iter1, iter2;
 
3545
   static char    *precs = 0, *prec1, *pret;
 
3546
   static int     cc;
 
3547
   
 
3548
   if (ftf)
 
3549
   {                                                                       //  get all window text
 
3550
      ftf = 0;
 
3551
      if (precs) g_free(precs);                                            //  free prior memory if there
 
3552
      zlock();
 
3553
      textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog));            //  get all text
 
3554
      gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2);
 
3555
      precs = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0);
 
3556
      prec1 = precs;                                                       //  1st record
 
3557
      zunlock();
 
3558
   }
 
3559
   
 
3560
   if (! precs || (*prec1 == 0))                                           //  no more records
 
3561
   {
 
3562
      if (precs) g_free(precs);
 
3563
      precs = 0;
 
3564
      return 0;
 
3565
   }
 
3566
 
 
3567
   cc = 0;
 
3568
   while ((prec1[cc] != 0) && (prec1[cc] != '\n')) cc++;                   //  scan for terminator
 
3569
   pret = prec1;
 
3570
   prec1 = prec1 + cc;                                                     //  next record
 
3571
   if (*prec1 == '\n') prec1++;
 
3572
   pret[cc] = 0;                                                           //  replace \n with 0
 
3573
   return pret;
 
3574
}
 
3575
 
 
3576
 
 
3577
//  dump text window into file
 
3578
//  return:  0: OK  +N: error
 
3579
 
 
3580
int   wfiledump_maxcc = 0;
 
3581
 
 
3582
int wfiledump(GtkWidget *mLog, char *filespec)
 
3583
{
 
3584
   FILE        *fid;
 
3585
   char        *prec;
 
3586
   int         ftf, err, cc;
 
3587
 
 
3588
   fid = fopen(filespec,"w");                                              //  open file
 
3589
   if (! fid) { 
 
3590
      zmessageACK(ZTX("cannot open file %s"),filespec);
 
3591
      return 1; 
 
3592
   }
 
3593
   
 
3594
   wfiledump_maxcc = 0;
 
3595
   
 
3596
   ftf = 1;
 
3597
   while (true)
 
3598
   {
 
3599
      prec = wscanf(mLog,ftf);                                             //  get text line
 
3600
      if (! prec) break;
 
3601
      fprintf(fid,"%s\n",prec);                                            //  output with \n
 
3602
      cc = strlen(prec);
 
3603
      if (cc > wfiledump_maxcc) wfiledump_maxcc = cc;
 
3604
   }
 
3605
   
 
3606
   err = fclose(fid);                                                      //  close file
 
3607
   if (err) { zmessageACK("file close error"); return 2; }
 
3608
   else return 0;
 
3609
}
 
3610
 
 
3611
 
 
3612
//  save text window to file, via file chooser dialog
 
3613
 
 
3614
void wfilesave(GtkWidget *mLog)
 
3615
{
 
3616
   int      err;
 
3617
   char     *file;
 
3618
 
 
3619
   file = zgetfile(ZTX("save screen to file"),"screen-save.txt","save");
 
3620
   if (! file) return;
 
3621
   err = wfiledump(mLog,file);
 
3622
   if (err) zmessageACK("save screen failed (%d)",err);
 
3623
   zfree(file);
 
3624
   return;
 
3625
}
 
3626
 
 
3627
 
 
3628
//  print text window to default printer
 
3629
//  use landscape mode if max. print line > A4 width
 
3630
 
 
3631
void wprintp(GtkWidget *mLog)
 
3632
{
 
3633
   int      pid, err;
 
3634
   char     tempfile[50], command[200];
 
3635
 
 
3636
   pid = getpid();
 
3637
   snprintf(tempfile,49,"/tmp/wprintp-%d",pid);
 
3638
   err = wfiledump(mLog,tempfile);
 
3639
   if (err) return;
 
3640
 
 
3641
   if (wfiledump_maxcc < 97)
 
3642
      snprintf(command,199,"lp -o %s -o %s -o %s -o %s -o %s -o %s %s",
 
3643
                     "cpi=14","lpi=8","page-left=50","page-top=50",
 
3644
                     "page-right=40","page-bottom=40",tempfile);
 
3645
 
 
3646
   else
 
3647
      snprintf(command,199,"lp -o %s -o %s -o %s -o %s -o %s -o %s -o %s %s",
 
3648
                     "landscape","cpi=14","lpi=8","page-left=50","page-top=50",
 
3649
                     "page-right=40","page-bottom=40",tempfile);
 
3650
 
 
3651
   err = system(command);
 
3652
   if (err) zmessLogACK("print error %s",wstrerror(err));
 
3653
   return;
 
3654
}
 
3655
 
 
3656
 
 
3657
/**************************************************************************
 
3658
   simplified GTK menu bar, tool bar, status bar functions
 
3659
***************************************************************************/
 
3660
 
 
3661
int            mbIconSize = 16;                                            //  valid during menu bar construction
 
3662
int            tbIconSize = 24;                                            //  valid during toolbar construction
 
3663
GtkTooltips    *tbTooltips = 0;                                            //  one instance for all toolbars
 
3664
 
 
3665
 
 
3666
//  create menu bar and add to vertical packing box
 
3667
 
 
3668
GtkWidget * create_menubar(GtkWidget *vbox, int iconsize)
 
3669
{
 
3670
   GtkWidget   *wmbar;
 
3671
 
 
3672
   wmbar = gtk_menu_bar_new(); 
 
3673
   gtk_box_pack_start(GTK_BOX(vbox),wmbar,0,0,0);
 
3674
   mbIconSize = iconsize;
 
3675
   return wmbar;
 
3676
}
 
3677
 
 
3678
 
 
3679
//  add menu item to menu bar
 
3680
 
 
3681
GtkWidget * add_menubar_item(GtkWidget *wmbar, const char *mname, mtFunc func)
 
3682
{
 
3683
   GtkWidget   *wmitem;
 
3684
 
 
3685
   wmitem = gtk_menu_item_new_with_label(mname);
 
3686
   gtk_menu_shell_append(GTK_MENU_SHELL(wmbar),wmitem);
 
3687
   if (func) G_SIGNAL(wmitem,"activate",func,mname);
 
3688
   return  wmitem;
 
3689
}
 
3690
 
 
3691
 
 
3692
//  add submenu item (with optional icon) to menu item 
 
3693
 
 
3694
GtkWidget * add_submenu_item(GtkWidget *wmitem, cchar *mlab, cchar *icon, mtFunc func)
 
3695
{
 
3696
   GtkWidget      *wmsub, *wmsubitem;
 
3697
   GError         **gerror = 0;
 
3698
   GdkPixbuf      *pixbuf;
 
3699
   GtkWidget      *wicon = 0;
 
3700
   char           iconpath[1000];
 
3701
 
 
3702
   wmsub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(wmitem));
 
3703
   if (wmsub == null) {
 
3704
      wmsub = gtk_menu_new();
 
3705
      gtk_menu_item_set_submenu(GTK_MENU_ITEM(wmitem),wmsub);
 
3706
   }
 
3707
 
 
3708
   if (icon) {
 
3709
      *iconpath = 0;
 
3710
      strncatv(iconpath,999,zicondir,"/",icon,null);
 
3711
      pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,mbIconSize,mbIconSize,1,gerror);
 
3712
      if (pixbuf) wicon = gtk_image_new_from_pixbuf(pixbuf);
 
3713
   }
 
3714
 
 
3715
   if (strEqu(mlab,"separator"))                                           //  v.2.16
 
3716
      wmsubitem = gtk_separator_menu_item_new();
 
3717
   else {
 
3718
      if (wicon) {
 
3719
         wmsubitem = gtk_image_menu_item_new_with_label(mlab);
 
3720
         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(wmsubitem),wicon);
 
3721
      }
 
3722
      else  wmsubitem = gtk_menu_item_new_with_label(mlab);
 
3723
   }
 
3724
 
 
3725
   gtk_menu_shell_append(GTK_MENU_SHELL(wmsub),wmsubitem);
 
3726
 
 
3727
   if (func) G_SIGNAL(wmsubitem,"activate",func,mlab);
 
3728
   return  wmsubitem;
 
3729
}
 
3730
 
 
3731
 
 
3732
//  create toolbar and add to vertical packing box
 
3733
 
 
3734
GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize, int vert)
 
3735
{
 
3736
   GtkWidget   *wtbar;
 
3737
 
 
3738
   wtbar = gtk_toolbar_new();
 
3739
   if (vert) gtk_toolbar_set_orientation(GTK_TOOLBAR(wtbar),GTK_ORIENTATION_VERTICAL);
 
3740
   gtk_box_pack_start(GTK_BOX(vbox),wtbar,0,0,0);
 
3741
   tbIconSize = iconsize;
 
3742
   
 
3743
   if (! tbTooltips) tbTooltips = gtk_tooltips_new();
 
3744
   return  wtbar;
 
3745
}
 
3746
 
 
3747
 
 
3748
//  add toolbar button with stock icon ("gtk-quit") or custom icon ("iconfile.png")
 
3749
 
 
3750
GtkWidget * add_toolbar_button(GtkWidget *wtbar, cchar *blab, cchar *btip, cchar *icon, mtFunc func)
 
3751
{
 
3752
   GtkToolItem    *tbutton;
 
3753
   GError         **gerror = 0;
 
3754
   GdkPixbuf      *pixbuf;
 
3755
   GtkWidget      *wicon = 0;
 
3756
   char           iconpath[1000];
 
3757
 
 
3758
   if (icon == null || *icon == 0) 
 
3759
      tbutton = gtk_tool_button_new(0,0);
 
3760
 
 
3761
   else if (strnEqu(icon,"gtk-",4)) 
 
3762
      tbutton = gtk_tool_button_new_from_stock(icon);
 
3763
 
 
3764
   else {
 
3765
      *iconpath = 0;
 
3766
      strncatv(iconpath,999,zicondir,"/",icon,null);
 
3767
      pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,tbIconSize,tbIconSize,1,gerror);
 
3768
      if (pixbuf) wicon = gtk_image_new_from_pixbuf(pixbuf);
 
3769
      if (wicon) tbutton = gtk_tool_button_new(wicon,0);
 
3770
      else  tbutton = gtk_tool_button_new_from_stock("gtk-missing-image");
 
3771
   }
 
3772
 
 
3773
   gtk_tool_button_set_label(GTK_TOOL_BUTTON(tbutton),blab);
 
3774
   if (btip) gtk_tool_item_set_tooltip(tbutton,tbTooltips,btip,"");
 
3775
   gtk_toolbar_insert(GTK_TOOLBAR(wtbar),GTK_TOOL_ITEM(tbutton),-1);
 
3776
   G_SIGNAL(tbutton,"clicked",func,blab);
 
3777
   return  (GtkWidget *) tbutton;
 
3778
}
 
3779
 
 
3780
 
 
3781
//  create a status bar and add to the start of a packing box
 
3782
 
 
3783
GtkWidget * create_stbar(GtkWidget *pbox)
 
3784
{
 
3785
   GtkWidget      *stbar;
 
3786
   static PangoFontDescription    *fontdesc;
 
3787
 
 
3788
   stbar = gtk_statusbar_new(); 
 
3789
   fontdesc = pango_font_description_from_string("Monospace 9");
 
3790
   gtk_widget_modify_font(stbar,fontdesc);                                 //  *** GTK does not work ***
 
3791
   gtk_box_pack_start(GTK_BOX(pbox),stbar,0,0,0);
 
3792
   gtk_widget_show(stbar);
 
3793
   return  stbar;
 
3794
}
 
3795
 
 
3796
 
 
3797
//  display message in status bar - callable from threads
 
3798
 
 
3799
int stbar_message(GtkWidget *wstbar, const char *message)
 
3800
{
 
3801
   static int     ctx = -1;
 
3802
 
 
3803
   zlock();
 
3804
   if (ctx == -1) ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(wstbar),"all");
 
3805
   gtk_statusbar_pop(GTK_STATUSBAR(wstbar),ctx);
 
3806
   gtk_statusbar_push(GTK_STATUSBAR(wstbar),ctx,message);
 
3807
   zmainloop();
 
3808
   zunlock();
 
3809
   return 0;
 
3810
}
 
3811
 
 
3812
 
 
3813
/**************************************************************************
 
3814
   simplified GTK dialog functions
 
3815
***************************************************************************/
 
3816
 
 
3817
//  private functions for interception of widget events and dialog completion
 
3818
 
 
3819
void  zdialog_widget_event(GtkWidget *, zdialog *zd);
 
3820
void  zdialog_response_event(GtkWidget *, int stat, zdialog *zd);
 
3821
 
 
3822
 
 
3823
//  create a new zdialog dialog
 
3824
//  optional arguments: list of 0-5 button names, null
 
3825
//  returned dialog status:  N = button N (1 to 5)
 
3826
//                          -4 = "X" destroy button
 
3827
 
 
3828
zdialog * zdialog_new(const char *title, GtkWidget *parent, ...)           //  parent added     v.2.5
 
3829
{
 
3830
   zdialog         *zd;
 
3831
   GtkWidget       *dialog;
 
3832
   char            *butt[5];
 
3833
   int             ii;
 
3834
   va_list         arglist;
 
3835
   GtkDialogFlags  flags = (GtkDialogFlags) 0;
 
3836
   GtkWindow       *wparent = (GtkWindow *) parent;
 
3837
 
 
3838
   zd = (zdialog *) zmalloc(sizeof(zdialog));
 
3839
   
 
3840
   for (ii = 0; ii < 5; ii++) butt[ii] = 0;
 
3841
 
 
3842
   va_start(arglist,parent);
 
3843
 
 
3844
   for (ii = 0; ii < 5; ii++)
 
3845
   {
 
3846
      butt[ii] = va_arg(arglist, char *);                                  //  get up to 5 buttons
 
3847
      if (! butt[ii]) break;
 
3848
   }
 
3849
 
 
3850
   va_end(arglist);
 
3851
 
 
3852
   zlock();                                                                //  create dialog box
 
3853
   dialog = gtk_dialog_new_with_buttons(title,wparent,flags,
 
3854
              butt[0],1, butt[1],2, butt[2],3, butt[3],4, butt[4],5, null);
 
3855
   gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox),5);
 
3856
   gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);          //  v.2.7.1
 
3857
 
 
3858
   zunlock();
 
3859
 
 
3860
   zd->sentinel = zdsentinel;                                              //  validity sentinel         v.2.2
 
3861
   zd->eventCB = 0;                                                        //  no user event callback
 
3862
   zd->complCB = 0;                                                        //  no user dialog completion callback
 
3863
   zd->zstat = 0;                                                          //  no zdialog status
 
3864
   zd->disabled = 1;                                                       //  widget signals disabled
 
3865
 
 
3866
   zd->widget[0].type = "dialog";                                          //  set up 1st widget = dialog
 
3867
   zd->widget[0].name = "dialog";
 
3868
   zd->widget[0].pname = 0;
 
3869
   zd->widget[0].data = strdupz(title);
 
3870
   zd->widget[0].cblist = 0;
 
3871
   zd->widget[0].widget = dialog;
 
3872
 
 
3873
   zd->widget[1].type = 0;                                                 //  eof - no contained widgets yet
 
3874
   return zd;
 
3875
}
 
3876
 
 
3877
 
 
3878
//  add widget to existing zdialog
 
3879
 
 
3880
int zdialog_add_widget (
 
3881
     zdialog *zd, const char *type, const char *name, const char *pname,   //  mandatory args
 
3882
     const char *data, int scc, int homog, int expand, int space)          //  optional args (default = 0)
 
3883
{
 
3884
   GtkWidget      *widget = 0, *pwidget = 0;
 
3885
   GtkTextBuffer  *editBuff = 0;
 
3886
   GdkColor       gdkcolor;
 
3887
   const char     *pp, *ptype = 0;
 
3888
   char           vdata[30];
 
3889
   double         min, max, step, val;
 
3890
   int            iiw, iip, kk, err;
 
3891
 
 
3892
   static PangoFontDescription    *monofont = 0;
 
3893
   
 
3894
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
3895
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
3896
 
 
3897
   for (iiw = 1; zd->widget[iiw].type; iiw++);                             //  find next avail. slot
 
3898
   if (iiw > zdmaxwidgets-2) zappcrash("too many widgets: %d",iiw);
 
3899
 
 
3900
   zd->widget[iiw].type = strdupz(type);                                   //  init. widget struct
 
3901
   zd->widget[iiw].name = strdupz(name);                                   //  all strings in nonvolatile mem
 
3902
   zd->widget[iiw].pname = strdupz(pname);
 
3903
   zd->widget[iiw].data = 0;
 
3904
   zd->widget[iiw].cblist = 0;
 
3905
   zd->widget[iiw].scc = scc;
 
3906
   zd->widget[iiw].homog = homog;
 
3907
   zd->widget[iiw].expand = expand;
 
3908
   zd->widget[iiw].space = space;
 
3909
   zd->widget[iiw].widget = 0;
 
3910
 
 
3911
   zd->widget[iiw+1].type = 0;                                             //  new EOF marker
 
3912
 
 
3913
   if (strcmpv(type,"dialog","hbox","vbox","hsep","vsep","frame","scrwin",
 
3914
                    "label","entry","edit","button","togbutt","check","combo",
 
3915
                    "comboE","radio","spin","hscale","vscale","colorbutt", null) == 0)
 
3916
      zappcrash("zdialog, bad widget type: %s",type);
 
3917
 
 
3918
   for (iip = iiw-1; iip >= 0; iip--)                                      //  find parent (container) widget
 
3919
      if (strEqu(pname,zd->widget[iip].name)) break;
 
3920
   if (iip < 0) zappcrash("zdialog, no parent for widget: %s",name);
 
3921
 
 
3922
   pwidget = zd->widget[iip].widget;                                       //  parent widget, type
 
3923
   ptype = zd->widget[iip].type;
 
3924
   
 
3925
   if (strcmpv(ptype,"dialog","hbox","vbox","frame","scrwin",null) == 0)
 
3926
      zappcrash("zdialog, bad widget parent type: %s",ptype);
 
3927
 
 
3928
   zlock();
 
3929
      
 
3930
   if (! monofont) monofont = pango_font_description_from_string("Monospace");
 
3931
 
 
3932
   if (strEqu(type,"hbox")) widget = gtk_hbox_new(homog,space);            //  expandable container boxes
 
3933
   if (strEqu(type,"vbox")) widget = gtk_vbox_new(homog,space);
 
3934
 
 
3935
   if (strEqu(type,"hsep")) widget = gtk_hseparator_new();                 //  horiz. & vert. separators
 
3936
   if (strEqu(type,"vsep")) widget = gtk_vseparator_new();
 
3937
         
 
3938
   if (strEqu(type,"frame")) {                                             //  frame around contained widgets
 
3939
      widget = gtk_frame_new(data);
 
3940
      gtk_frame_set_shadow_type(GTK_FRAME(widget),GTK_SHADOW_IN);
 
3941
   }
 
3942
 
 
3943
   if (strEqu(type,"scrwin")) {                                            //  scrolled window container
 
3944
      widget = gtk_scrolled_window_new(0,0);
 
3945
      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),          //  v.2.10
 
3946
                        GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
 
3947
   }
 
3948
 
 
3949
   if (strEqu(type,"label")) widget = gtk_label_new(data);                 //  label (static text)
 
3950
 
 
3951
   if (strEqu(type,"entry")) {                                             //  1-line text entry
 
3952
      widget = gtk_entry_new();
 
3953
      if (data) gtk_entry_set_text(GTK_ENTRY(widget),data);
 
3954
      if (scc) gtk_entry_set_width_chars(GTK_ENTRY(widget),scc);
 
3955
      gtk_widget_modify_font(widget,monofont);
 
3956
      G_SIGNAL(widget,"changed",zdialog_widget_event,zd)
 
3957
   }
 
3958
      
 
3959
   if (strEqu(type,"edit")) {                                              //  multiline edit box
 
3960
      widget = gtk_text_view_new();
 
3961
      editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
 
3962
      if (data) gtk_text_buffer_set_text(editBuff,data,-1);
 
3963
      gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1);
 
3964
      gtk_widget_modify_font(widget,monofont);
 
3965
      G_SIGNAL(editBuff,"changed",zdialog_widget_event,zd)                 //  buffer signals, not widget
 
3966
   }
 
3967
      
 
3968
   if (strEqu(type,"button")) {                                            //  button
 
3969
      widget = gtk_button_new_with_label(data);
 
3970
      G_SIGNAL(widget,"clicked",zdialog_widget_event,zd)
 
3971
   }
 
3972
 
 
3973
   if (strEqu(type,"togbutt")) {                                           //  toggle button
 
3974
      widget = gtk_toggle_button_new_with_label(data);
 
3975
      G_SIGNAL(widget,"toggled",zdialog_widget_event,zd)
 
3976
   }
 
3977
 
 
3978
   if (strEqu(type,"check")) {                                             //  checkbox
 
3979
      if (data) widget = gtk_check_button_new_with_label(data);
 
3980
      else  widget = gtk_check_button_new();
 
3981
      G_SIGNAL(widget,"toggled",zdialog_widget_event,zd)
 
3982
   }
 
3983
      
 
3984
   if (strEqu(type,"combo")) {                                             //  combo box
 
3985
      widget = gtk_combo_box_new_text();
 
3986
      zd->widget[iiw].cblist = pvlist_create(zdcbmax);                     //  for drop-down list
 
3987
      if (! blank_null(data)) {
 
3988
         pvlist_append(zd->widget[iiw].cblist,data);                       //  add data to drop-down list
 
3989
         gtk_combo_box_append_text(GTK_COMBO_BOX(widget),data);
 
3990
         gtk_combo_box_set_active(GTK_COMBO_BOX(widget),0);
 
3991
      }
 
3992
      gtk_widget_modify_font(widget,monofont);
 
3993
      G_SIGNAL(widget,"changed",zdialog_widget_event,zd)
 
3994
   }
 
3995
 
 
3996
   if (strEqu(type,"comboE")) {                                            //  combo box with entry box
 
3997
      widget = gtk_combo_box_entry_new_text();
 
3998
      zd->widget[iiw].cblist = pvlist_create(zdcbmax);                     //  for drop-down list
 
3999
      if (! blank_null(data)) {
 
4000
         gtk_entry_set_text(GTK_ENTRY(GTK_BIN(widget)->child),data);       //  entry = initial data
 
4001
         pvlist_append(zd->widget[iiw].cblist,data);                       //  add data to drop-down list
 
4002
         gtk_combo_box_append_text(GTK_COMBO_BOX(widget),data);
 
4003
      }
 
4004
      gtk_widget_modify_font(widget,monofont);
 
4005
      G_SIGNAL(widget,"changed",zdialog_widget_event,zd)
 
4006
   }
 
4007
      
 
4008
   if (strEqu(type,"radio")) {                                             //  radio button
 
4009
      for (kk = iip+1; kk <= iiw; kk++) 
 
4010
         if (strEqu(zd->widget[kk].pname,pname) &&                         //  find first radio button
 
4011
             strEqu(zd->widget[kk].type,"radio")) break;                   //    with same container
 
4012
      if (kk == iiw) 
 
4013
         widget = gtk_radio_button_new_with_label(null,data);              //  this one is first
 
4014
      else 
 
4015
         widget = gtk_radio_button_new_with_label_from_widget              //  not first, add to group
 
4016
              (GTK_RADIO_BUTTON(zd->widget[kk].widget),data);
 
4017
      G_SIGNAL(widget,"toggled",zdialog_widget_event,zd)
 
4018
   }
 
4019
 
 
4020
   if (strcmpv(type,"spin","hscale","vscale",null)) {                      //  spin button or sliding scale
 
4021
      pp = strField(data,'|',1); err = convSD(pp,min);                     //  locale fix
 
4022
      pp = strField(data,'|',2); err += convSD(pp,max);
 
4023
      pp = strField(data,'|',3); err += convSD(pp,step);
 
4024
      pp = strField(data,'|',4); err += convSD(pp,val);
 
4025
      if (err) { min = 0; max = 100; step = 1; val = 50; }
 
4026
 
 
4027
      if (*type == 's') {
 
4028
         widget = gtk_spin_button_new_with_range(min,max,step);
 
4029
         gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),val);
 
4030
      }
 
4031
      if (*type == 'h') {
 
4032
         widget = gtk_hscale_new_with_range(min,max,step);
 
4033
         gtk_range_set_value(GTK_RANGE(widget),val);
 
4034
         gtk_scale_set_draw_value(GTK_SCALE(widget),0);
 
4035
      }
 
4036
      if (*type == 'v') {
 
4037
         widget = gtk_vscale_new_with_range(min,max,step);
 
4038
         gtk_range_set_value(GTK_RANGE(widget),val);
 
4039
         gtk_scale_set_draw_value(GTK_SCALE(widget),0);
 
4040
      }
 
4041
      G_SIGNAL(widget,"value-changed",zdialog_widget_event,zd)
 
4042
      sprintf(vdata,"%g",val);
 
4043
      data = vdata;
 
4044
   }
 
4045
   
 
4046
   if (strEqu(type,"colorbutt")) {                                         //  color edit button        v.2.17
 
4047
      if (! data) data = "0|0|0";                                          //  data format: "nnn|nnn|nnn" = RGB
 
4048
      pp = strField(data,'|',1); gdkcolor.red = 256 * atoi(pp);
 
4049
      pp = strField(data,'|',2); gdkcolor.green = 256 * atoi(pp);
 
4050
      pp = strField(data,'|',3); gdkcolor.blue = 256 * atoi(pp);
 
4051
      widget = gtk_color_button_new_with_color(&gdkcolor);
 
4052
      G_SIGNAL(widget,"color-set",zdialog_widget_event,zd)
 
4053
   }
 
4054
      
 
4055
   //  all widget types come here
 
4056
 
 
4057
   zd->widget[iiw].widget = widget;                                        //  set widget in zdialog
 
4058
 
 
4059
   if (strEqu(ptype,"hbox") || strEqu(ptype,"vbox"))                       //  add to hbox/vbox
 
4060
      gtk_box_pack_start(GTK_BOX(pwidget),widget,expand,expand,space);
 
4061
   if (strEqu(ptype,"frame"))                                              //  add to frame
 
4062
      gtk_container_add(GTK_CONTAINER(pwidget),widget);
 
4063
   if (strEqu(ptype,"scrwin"))                                             //  add to scroll window
 
4064
      gtk_container_add(GTK_CONTAINER(pwidget),widget);
 
4065
   if (strEqu(ptype,"dialog"))                                             //  add to dialog box
 
4066
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pwidget)->vbox),
 
4067
                                   widget,expand,expand,space);
 
4068
   if (data) zd->widget[iiw].data = strdupz(data);                         //  data is nonvolatile memory
 
4069
 
 
4070
   zunlock();
 
4071
   return 0;
 
4072
}
 
4073
 
 
4074
 
 
4075
//  add widget to existing zdialog - alternative form (clearer and easier code)
 
4076
//  options: "scc=nn | homog | expand | space=nn"  (all optional, any order)
 
4077
 
 
4078
int zdialog_add_widget(zdialog *zd, const char *type, const char *name, 
 
4079
                       const char *parent, const char *data, const char *options)                                
 
4080
{
 
4081
   int      stat, scc = 0, homog = 0, expand = 0, space = 0, begin = 1;
 
4082
   char     pname[8];
 
4083
   double   pval;
 
4084
   
 
4085
   while (true)
 
4086
   {
 
4087
      stat = strParms(begin,options,pname,8,pval);
 
4088
      if (stat == -1) break;
 
4089
      if (stat == 1) zappcrash("bad zdialog options: %s",options);
 
4090
      if (strEqu(pname,"scc")) scc = (int(pval));
 
4091
      else if (strEqu(pname,"homog")) homog = 1;
 
4092
      else if (strEqu(pname,"expand")) expand = 1;
 
4093
      else if (strEqu(pname,"space")) space = (int(pval));
 
4094
      else zappcrash("bad zdialog options: %s",options);
 
4095
   }
 
4096
   
 
4097
   stat = zdialog_add_widget(zd,type,name,parent,data,scc,homog,expand,space);
 
4098
   return stat;
 
4099
}
 
4100
 
 
4101
 
 
4102
//  resize dialog to a size greater than initial size
 
4103
//  (as determined by the included widgets)
 
4104
 
 
4105
int zdialog_resize(zdialog *zd, int width, int height)
 
4106
{
 
4107
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4108
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4109
 
 
4110
   zlock();
 
4111
   GtkWidget *window = zd->widget[0].widget;
 
4112
   gtk_window_set_default_size(GTK_WINDOW(window),width,height);
 
4113
   zunlock();
 
4114
   return 1;
 
4115
}
 
4116
 
 
4117
 
 
4118
//  put data into a zdialog widget
 
4119
 
 
4120
int zdialog_put_data(zdialog *zd, const char *name, const char *data)
 
4121
{
 
4122
   GtkWidget      *widget;
 
4123
   GtkTextBuffer  *textBuff;
 
4124
   GdkColor       gdkcolor;
 
4125
   int            iiw, iip, nn, kk;
 
4126
   const char     *type, *pname, *pp;
 
4127
   char           *wdata;
 
4128
   double         val;
 
4129
   
 
4130
   if (! zd || zd->sentinel != zdsentinel) {                               //  detect destroyed dialog  v.2.2
 
4131
      printf("zdialog_put_data(%s,%s), zdialog invalid \n",name,data);
 
4132
      return 0;
 
4133
   }
 
4134
   
 
4135
   for (iiw = 1; zd->widget[iiw].type; iiw++)                              //  find widget
 
4136
      if (strEqu(zd->widget[iiw].name,name)) break;
 
4137
   if (! zd->widget[iiw].type) return 0;
 
4138
   
 
4139
   type = zd->widget[iiw].type;
 
4140
   widget = zd->widget[iiw].widget;
 
4141
   pname = zd->widget[iiw].pname;
 
4142
 
 
4143
   wdata = zd->widget[iiw].data;
 
4144
   if (wdata) zfree(wdata);                                                //  free prior data memory
 
4145
   zd->widget[iiw].data = 0;
 
4146
 
 
4147
   if (data) {
 
4148
      wdata = strdupz(data);                                               //  set new data for widget
 
4149
      zd->widget[iiw].data = wdata;
 
4150
      if (utf8_check(wdata))
 
4151
         printf("zdialog: bad UTF8 encoding %s \n",wdata);                 //  v.2.4
 
4152
   }
 
4153
   
 
4154
   zd->disabled++;                                                         //  disable for widget stuffing  v.2.9
 
4155
 
 
4156
   zlock();   
 
4157
 
 
4158
   if (strEqu(type,"label")) 
 
4159
      gtk_label_set_text(GTK_LABEL(widget),data);
 
4160
 
 
4161
   if (strEqu(type,"entry")) 
 
4162
      gtk_entry_set_text(GTK_ENTRY(widget),data);
 
4163
 
 
4164
   if (strEqu(type,"button"))                                              //  change button label  v.2.21
 
4165
      gtk_button_set_label(GTK_BUTTON(widget),data);
 
4166
 
 
4167
   if (strEqu(type,"edit")) {
 
4168
      textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
 
4169
      gtk_text_buffer_set_text(textBuff,data,-1);
 
4170
   }
 
4171
   
 
4172
   if (strcmpv(type,"togbutt","check","radio",null)) 
 
4173
   {
 
4174
      if (! data) kk = nn = 0;
 
4175
      else kk = convSI(data,nn);
 
4176
      if (kk != 0) nn = 0;                                                 //  data not integer, force zero
 
4177
      if (nn <= 0) nn = 0; else nn = 1;
 
4178
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),nn);          //  set gtk widget value
 
4179
 
 
4180
      for (iip = 1; zd->widget[iip].type; iip++)                           //  find widgets of same type 
 
4181
      {                                                                    //    having same parent       v.2.0
 
4182
         if (iip == iiw) continue;
 
4183
         if (strEqu(zd->widget[iip].pname,pname) && strEqu(zd->widget[iip].type,type)) 
 
4184
         {
 
4185
            wdata = zd->widget[iip].data;
 
4186
            if (wdata) zfree(wdata);                                       //  free prior data memory
 
4187
            if (nn == 0) zd->widget[iip].data = strdupz("1");
 
4188
            else  zd->widget[iip].data = strdupz("0");                     //  family members = opposite  v.2.0
 
4189
         }
 
4190
      }
 
4191
   }
 
4192
   
 
4193
   if (strEqu(type,"spin")) {
 
4194
      kk = convSD(data,val);
 
4195
      if (kk != 0) val = 0.0;
 
4196
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),val);
 
4197
   }
 
4198
   
 
4199
   if (strEqu(type,"colorbutt")) {                                         //  color button        v.2.17
 
4200
      pp = strField(data,'|',1); gdkcolor.red = 256 * atoi(pp);
 
4201
      pp = strField(data,'|',2); gdkcolor.green = 256 * atoi(pp);
 
4202
      pp = strField(data,'|',3); gdkcolor.blue = 256 * atoi(pp);
 
4203
      gtk_color_button_set_color(GTK_COLOR_BUTTON(widget),&gdkcolor);
 
4204
   }
 
4205
   
 
4206
   if (strcmpv(type,"hscale","vscale",null)) {
 
4207
      kk = convSD(data,val);
 
4208
      if (kk != 0) val = 0.0;
 
4209
      gtk_range_set_value(GTK_RANGE(widget),val);
 
4210
   }
 
4211
   
 
4212
   if (strEqu(type,"combo")) {
 
4213
      if (! blank_null(data)) {
 
4214
         kk = pvlist_prepend(zd->widget[iiw].cblist,data,1);               //  add to drop-down list
 
4215
         if (kk == 0)                                                      //  (only if unique)
 
4216
            gtk_combo_box_prepend_text(GTK_COMBO_BOX(widget),data);
 
4217
         kk = pvlist_find(zd->widget[iiw].cblist,data);
 
4218
         gtk_combo_box_set_active(GTK_COMBO_BOX(widget),kk);               //  make the active entry   v.2.7
 
4219
      }
 
4220
      else gtk_combo_box_set_active(GTK_COMBO_BOX(widget),-1);             //  make no active entry
 
4221
   }
 
4222
 
 
4223
   if (strEqu(type,"comboE")) {
 
4224
      if (! blank_null(data)) {
 
4225
         kk = pvlist_prepend(zd->widget[iiw].cblist,data,1);               //  add to drop-down list
 
4226
         if (kk == 0)                                                      //  (only if unique)
 
4227
            gtk_combo_box_prepend_text(GTK_COMBO_BOX(widget),data);
 
4228
         gtk_entry_set_text(GTK_ENTRY(GTK_BIN(widget)->child),data);       //  stuff entry box with new data
 
4229
      }
 
4230
      else gtk_entry_set_text(GTK_ENTRY(GTK_BIN(widget)->child),"");       //  stuff entry box with nothing
 
4231
   }
 
4232
 
 
4233
   zunlock();
 
4234
   zd->disabled--;                                                         //  re-enable dialog   v.2.9
 
4235
   return iiw;
 
4236
}
 
4237
 
 
4238
 
 
4239
//  get data from a dialog widget based on its name
 
4240
 
 
4241
const char * zdialog_get_data(zdialog *zd, const char *name)
 
4242
{
 
4243
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4244
   if (zd->sentinel != zdsentinel) return 0;
 
4245
 
 
4246
   for (int ii = 1; zd->widget[ii].type; ii++)
 
4247
      if (strEqu(zd->widget[ii].name,name)) 
 
4248
            return zd->widget[ii].data;
 
4249
   return 0;
 
4250
}
 
4251
 
 
4252
 
 
4253
//  get GTK widget from zdialog and widget name
 
4254
 
 
4255
GtkWidget * zdialog_widget(zdialog *zd, const char *name)
 
4256
{
 
4257
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4258
   if (zd->sentinel != zdsentinel) return 0;
 
4259
 
 
4260
   for (int ii = 1; zd->widget[ii].type; ii++)
 
4261
   if (strEqu(zd->widget[ii].name,name)) return zd->widget[ii].widget;
 
4262
   return 0;
 
4263
}
 
4264
 
 
4265
 
 
4266
//  run the zdialog
 
4267
//  if modal, return after complete with status of button (OK, cancel ...)
 
4268
//  if not modal, return immediately with dialog active
 
4269
 
 
4270
int zdialog_run(zdialog *zd, zdialog_event evfunc, zdialog_compl compfunc)
 
4271
{
 
4272
   int         ii, zstat;
 
4273
   GtkWidget   *widget, *dialog;
 
4274
 
 
4275
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4276
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4277
 
 
4278
   zlock();
 
4279
 
 
4280
   dialog = zd->widget[0].widget;
 
4281
   gtk_widget_show_all(dialog);                                            //  activate dialog
 
4282
 
 
4283
   for (ii = 1; zd->widget[ii].type; ii++)                                 //  *** stop auto-selection
 
4284
   {                                                                       //  (GTK "feature")
 
4285
      if (strEqu(zd->widget[ii].type,"entry")) {
 
4286
         widget = zd->widget[ii].widget;
 
4287
         gtk_editable_set_position(GTK_EDITABLE(widget),-1);
 
4288
         break;
 
4289
      }
 
4290
 
 
4291
      if (strEqu(zd->widget[ii].type,"comboE")) {                          //  bugfix, remove "combo"  v.2.8
 
4292
         widget = zd->widget[ii].widget;
 
4293
         gtk_editable_set_position(GTK_EDITABLE(GTK_BIN(widget)->child),-1);
 
4294
         break;
 
4295
      }
 
4296
   }
 
4297
 
 
4298
   if (evfunc) zd->eventCB = (void *) evfunc;                              //  link to user event callback
 
4299
 
 
4300
   if (zd->disabled) zd->disabled--;                                       //  enable widget events   v.2.23
 
4301
 
 
4302
   if (compfunc) {
 
4303
      G_SIGNAL(dialog,"response",zdialog_response_event,zd);               //  internal dialog response function
 
4304
      zd->complCB = (void *) compfunc;                                     //  link to user completion callback
 
4305
      zunlock();
 
4306
      return 0;                                                            //  return now, dialog is non-modal
 
4307
   }
 
4308
 
 
4309
   else zstat = gtk_dialog_run(GTK_DIALOG(dialog));                        //  modal dialog, return when complete
 
4310
   zd->zstat = zstat;                                                      //  set zdialog status (from button)
 
4311
   zunlock();
 
4312
   return zstat;                                                           //  and return status
 
4313
}
 
4314
 
 
4315
 
 
4316
//  zdialog event handler - private function called when a widget is edited.
 
4317
//  Updates data in zdialog, calls user callback function (if present).
 
4318
//  This function always runs in main() thread, so zlock() unnecessary.
 
4319
 
 
4320
void zdialog_widget_event(GtkWidget *widget, zdialog *zd)
 
4321
{
 
4322
   GtkTextView       *textView = 0;
 
4323
   GtkTextBuffer     *textBuff = 0;
 
4324
   GtkTextIter       iter1, iter2;
 
4325
   GdkColor          gdkcolor;
 
4326
   static GtkWidget  *lastwidget = 0;
 
4327
   int               ii, nn;
 
4328
   const char        *name, *type, *wdata;
 
4329
   char              sdata[20];
 
4330
   double            dval;
 
4331
   static int        cbadded = 0;
 
4332
 
 
4333
   zdialog_event  *callbackfunc = 0;                                       //  user event callback function
 
4334
 
 
4335
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4336
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4337
 
 
4338
   if (zd->disabled) return;                                               //  stop re-entrance from own updates
 
4339
 
 
4340
   for (ii = 1; zd->widget[ii].type; ii++)                                 //  find widget in zdialog
 
4341
      if (zd->widget[ii].widget == widget) goto found_widget;
 
4342
 
 
4343
   for (ii = 1; zd->widget[ii].type; ii++) {                               //  failed, test if buffer
 
4344
      if (strEqu(zd->widget[ii].type,"edit")) {                            //    of text view widget
 
4345
         textView = GTK_TEXT_VIEW(zd->widget[ii].widget);
 
4346
         textBuff = gtk_text_view_get_buffer(textView);
 
4347
         if (widget == (GtkWidget *) textBuff) goto found_widget;
 
4348
      }
 
4349
   }
 
4350
 
 
4351
   return;                                                                 //  widget not found, ignore
 
4352
 
 
4353
found_widget:
 
4354
 
 
4355
   zd->disabled++;                                                         //  stop re-entrance from own updates
 
4356
                                                                           //        v.2.9
 
4357
   name = zd->widget[ii].name;
 
4358
   type = zd->widget[ii].type;
 
4359
   wdata = 0;
 
4360
 
 
4361
   if (strEqu(type,"button")) wdata = "clicked";
 
4362
 
 
4363
   if (strEqu(type,"entry"))
 
4364
         wdata = gtk_entry_get_text(GTK_ENTRY(widget));
 
4365
         
 
4366
   if (strEqu(type,"edit")) {
 
4367
      gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2);
 
4368
      wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0);
 
4369
   }
 
4370
 
 
4371
   if (strcmpv(type,"radio","check","togbutt",null)) 
 
4372
   {
 
4373
      nn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 
4374
      if (nn == 0) wdata = "0";
 
4375
      else wdata = "1";
 
4376
   }
 
4377
 
 
4378
   if (strEqu(type,"combo"))
 
4379
         wdata = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
 
4380
 
 
4381
   if (strEqu(type,"comboE"))
 
4382
   {
 
4383
      if (widget == lastwidget && cbadded) {
 
4384
         pvlist_remove(zd->widget[ii].cblist,0);                           //  detect multiple edits (keystrokes)
 
4385
         gtk_combo_box_remove_text(GTK_COMBO_BOX(widget),0);               //    and replace prior entry with new
 
4386
      }
 
4387
      wdata = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(widget)->child));
 
4388
      cbadded = 0;
 
4389
      if (! blank_null(wdata)) {
 
4390
         nn = pvlist_prepend(zd->widget[ii].cblist,wdata,1);               //  add entry to drop-down list
 
4391
         if (nn == 0) {                                                    //  (only if unique)
 
4392
            gtk_combo_box_prepend_text(GTK_COMBO_BOX(widget),wdata);
 
4393
            cbadded = 1;
 
4394
         }
 
4395
      }
 
4396
   }
 
4397
   
 
4398
   if (strEqu(type,"spin"))
 
4399
   {
 
4400
      dval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
 
4401
      sprintf(sdata,"%g",dval);
 
4402
      wdata = sdata;
 
4403
   }
 
4404
   
 
4405
   if (strEqu(type,"colorbutt"))                                           //  color button        v.2.17
 
4406
   {
 
4407
      gtk_color_button_get_color(GTK_COLOR_BUTTON(widget),&gdkcolor);
 
4408
      sprintf(sdata,"%d|%d|%d",gdkcolor.red/256,gdkcolor.green/256,gdkcolor.blue/256);
 
4409
      wdata = sdata;
 
4410
   }
 
4411
   
 
4412
   if (strcmpv(type,"hscale","vscale",null))
 
4413
   {
 
4414
      dval = gtk_range_get_value(GTK_RANGE(widget));
 
4415
      sprintf(sdata,"%g",dval);
 
4416
      wdata = sdata;
 
4417
   }
 
4418
   
 
4419
   //  all widgets come here
 
4420
 
 
4421
   if (zd->widget[ii].data) zfree(zd->widget[ii].data);                    //  clear prior data
 
4422
   zd->widget[ii].data = 0;
 
4423
 
 
4424
   if (wdata) zd->widget[ii].data = strdupz(wdata);                        //  set new data
 
4425
   
 
4426
   lastwidget = widget;                                                    //  remember last widget updated
 
4427
 
 
4428
   if (zd->eventCB) {
 
4429
      callbackfunc = (zdialog_event *) zd->eventCB;                        //  do user callback function
 
4430
      callbackfunc(zd,name);
 
4431
   }
 
4432
 
 
4433
   zd->disabled--;
 
4434
   return;
 
4435
}
 
4436
 
 
4437
 
 
4438
//  zdialog response handler - private function called when dialog is completed.
 
4439
//  status corresponds to completion button from user (OK, cancel ...)
 
4440
 
 
4441
void zdialog_response_event(GtkWidget *, int zstat, zdialog *zd)
 
4442
{
 
4443
   zdialog_compl  *callbackfunc = 0;
 
4444
   
 
4445
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4446
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4447
 
 
4448
   zd->zstat = zstat;                                                      //  set zdialog status 
 
4449
 
 
4450
   if (zd->complCB) {
 
4451
      callbackfunc = (zdialog_compl *) zd->complCB;                        //  do user callback function
 
4452
      callbackfunc(zd,zstat);
 
4453
   }
 
4454
 
 
4455
   return;
 
4456
}
 
4457
 
 
4458
 
 
4459
//  send an event to an active dialog
 
4460
//  returns:  0 = no active dialog, 1 = OK
 
4461
 
 
4462
int zdialog_send_event(zdialog *zd, cchar *event)                          //  new v.2.17
 
4463
{
 
4464
   zdialog_event * evfunc = 0;
 
4465
 
 
4466
   zlock();                                                                //  v.2.22
 
4467
   
 
4468
   if (zd && zd->sentinel == zdsentinel) {                                 //  check dialog is active
 
4469
      evfunc = (zdialog_event *) zd->eventCB;
 
4470
      if (evfunc) evfunc(zd,event);                                        //  call dialog event function
 
4471
      else { zunlock(); return 0; }
 
4472
   }
 
4473
   else { zunlock(); return 0; }
 
4474
 
 
4475
   zunlock();
 
4476
   return 1;
 
4477
}
 
4478
 
 
4479
 
 
4480
//  Complete a dialog and give it a status, without user action.
 
4481
//  Dialog completion function or blocked caller will run.
 
4482
//  returns:  0 = no active dialog, 1 = OK
 
4483
 
 
4484
int zdialog_send_response(zdialog *zd, int zstat)                          //  new v.2.23
 
4485
{
 
4486
   if (! zd) return 0;                                                     //  detect destroyed dialog
 
4487
   if (zd->sentinel != zdsentinel) return 0;
 
4488
 
 
4489
   zlock();
 
4490
   gtk_dialog_response(GTK_DIALOG(zd->widget[0].widget), zstat);
 
4491
   zunlock();
 
4492
   
 
4493
   return 1;
 
4494
}
 
4495
 
 
4496
 
 
4497
//  Destroy the zdialog - must be done by zdialog_run() caller
 
4498
//  (else dialog continues active even after completion button).
 
4499
//  Data in widgets remains valid until zdialog_free() is called.
 
4500
 
 
4501
int zdialog_destroy(zdialog *zd)
 
4502
{
 
4503
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4504
   if (zd->sentinel != zdsentinel) return 0;
 
4505
 
 
4506
   zlock();                                                                //  bugfix  v.2.13
 
4507
   if (zd->widget[0].widget)                                               //  multiple destroys OK   v.2.7.1
 
4508
      gtk_widget_destroy(zd->widget[0].widget);                            //  destroy GTK dialog
 
4509
   zd->widget[0].widget = 0;
 
4510
   zunlock();
 
4511
 
 
4512
   zmainloop();                                                            //  avoid GTK bug   v.2.12
 
4513
   return 1;
 
4514
}
 
4515
 
 
4516
 
 
4517
//  free zdialog memory (destroy first, if not already)
 
4518
 
 
4519
int zdialog_free(zdialog *zd)
 
4520
{
 
4521
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4522
   if (zd->sentinel != zdsentinel) return 0;
 
4523
   
 
4524
   zdialog_destroy(zd);                                                    //  destroy GTK dialog if there
 
4525
 
 
4526
   zd->sentinel = 0;                                                       //  mark invalid           v.2.2
 
4527
 
 
4528
   for (int ii = 1; zd->widget[ii].type; ii++)                             //  loop through widgets
 
4529
   {
 
4530
      zfree((char *) zd->widget[ii].type);                                 //  free strings
 
4531
      zfree((char *) zd->widget[ii].name);
 
4532
      zfree((char *) zd->widget[ii].pname);
 
4533
      if (zd->widget[ii].data) zfree(zd->widget[ii].data);                 //  free data 
 
4534
      if (strcmpv(zd->widget[ii].type,"combo","comboE",0))                 //  if combo box, free drop-down list
 
4535
         pvlist_free(zd->widget[ii].cblist);
 
4536
   }
 
4537
   
 
4538
   zfree(zd);                                                              //  free zdialog memory
 
4539
   return 1;
 
4540
}
 
4541
 
 
4542
 
 
4543
//  put cursor at named widget
 
4544
 
 
4545
int zdialog_goto(zdialog *zd, const char *name)                            //  v.2.23
 
4546
{
 
4547
   GtkWidget   *widget; 
 
4548
   
 
4549
   widget = zdialog_widget(zd, name);
 
4550
   if (! widget) return 0;
 
4551
   gtk_editable_select_region(GTK_EDITABLE(widget),0,-1);                  //  focus on widget
 
4552
   gtk_widget_grab_focus(widget);
 
4553
   return 1;
 
4554
}
 
4555
 
 
4556
 
 
4557
//  convenience functions for stuffing and retrieving widget data
 
4558
 
 
4559
int zdialog_stuff(zdialog *zd, const char *name, const char *data)         //  stuff a string
 
4560
{
 
4561
   zdialog_put_data(zd, name, data);
 
4562
   return 1;
 
4563
}
 
4564
 
 
4565
int zdialog_stuff(zdialog *zd, const char *name, int idata)                //  stuff an integer
 
4566
{
 
4567
   char  string[16];
 
4568
 
 
4569
   sprintf(string,"%d",idata);
 
4570
   zdialog_put_data(zd,name,string);
 
4571
   return 1;
 
4572
}
 
4573
 
 
4574
int zdialog_stuff(zdialog *zd, const char *name, double ddata)             //  stuff a double
 
4575
{
 
4576
   char  string[32];
 
4577
   
 
4578
   snprintf(string,31,"%g",ddata);                                         //  outputs decimal point or comma
 
4579
   zdialog_put_data(zd,name,string);                                       //  (per locale)
 
4580
   return 1;
 
4581
}
 
4582
 
 
4583
int zdialog_fetch(zdialog *zd, const char *name, char *data, int maxcc)    //  fetch string data
 
4584
{
 
4585
   const char  *zdata;
 
4586
 
 
4587
   zdata = zdialog_get_data(zd,name);
 
4588
   if (! zdata) {
 
4589
      *data = 0;
 
4590
      return 0;
 
4591
   }
 
4592
   
 
4593
   return strncpy0(data,zdata,maxcc);                                      //  0 = OK, 1 = truncation  v.2.4
 
4594
}
 
4595
 
 
4596
int zdialog_fetch(zdialog *zd, const char *name, int &idata)               //  fetch an integer
 
4597
{
 
4598
   const char  *zdata;
 
4599
 
 
4600
   zdata = zdialog_get_data(zd,name);
 
4601
   if (! zdata) {
 
4602
      idata = 0;
 
4603
      return 0;
 
4604
   }
 
4605
   
 
4606
   idata = atoi(zdata);
 
4607
   return 1;
 
4608
}
 
4609
 
 
4610
int zdialog_fetch(zdialog *zd, const char *name, double &ddata)            //  fetch a double
 
4611
{
 
4612
   int         stat;
 
4613
   const char  *zdata;
 
4614
 
 
4615
   zdata = zdialog_get_data(zd,name);
 
4616
   if (! zdata) {
 
4617
      ddata = 0;
 
4618
      return 0;
 
4619
   }
 
4620
   
 
4621
   stat = convSD(zdata,ddata);                                             //  period or comma decimal point OK
 
4622
   if (stat < 4) return 1;
 
4623
   return 0;
 
4624
}
 
4625
 
 
4626
 
 
4627
//  append new item to combo box list without changing entry box
 
4628
 
 
4629
int zdialog_cb_app(zdialog *zd, const char *name, const char *data)
 
4630
{
 
4631
   int         ii, nn;
 
4632
 
 
4633
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4634
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4635
 
 
4636
   if (blank_null(data)) return 0;                                         //  find widget
 
4637
   for (ii = 1; zd->widget[ii].type; ii++) 
 
4638
      if (strEqu(zd->widget[ii].name,name)) break;
 
4639
   if (! zd->widget[ii].type) return 0;                                    //  not found
 
4640
   if (! strcmpv(zd->widget[ii].type,"combo","comboE",0)) return 0;        //  not combo box
 
4641
 
 
4642
   nn = pvlist_append(zd->widget[ii].cblist,data,1);                       //  append unique
 
4643
   if (nn >= 0) 
 
4644
      gtk_combo_box_append_text(GTK_COMBO_BOX(zd->widget[ii].widget),data);
 
4645
   return 1;
 
4646
}
 
4647
 
 
4648
 
 
4649
//  prepend new item to combo box list without changing entry box
 
4650
 
 
4651
int zdialog_cb_prep(zdialog *zd, const char *name, const char *data)
 
4652
{
 
4653
   int         ii, nn;
 
4654
 
 
4655
   if (! zd) zappcrash("zdialog null pointer");                            //  detect destroyed dialog  v.2.2
 
4656
   if (zd->sentinel != zdsentinel) zappcrash("zdialog not valid");
 
4657
 
 
4658
   if (blank_null(data)) return 0;                                         //  find widget
 
4659
   for (ii = 1; zd->widget[ii].type; ii++) 
 
4660
      if (strEqu(zd->widget[ii].name,name)) break;
 
4661
   if (! zd->widget[ii].type) return 0;                                    //  not found
 
4662
   if (! strcmpv(zd->widget[ii].type,"combo","comboE",0)) return 0;        //  not combo box
 
4663
 
 
4664
   nn = pvlist_prepend(zd->widget[ii].cblist,data,1);                      //  append unique
 
4665
   if (nn == 0) 
 
4666
      gtk_combo_box_prepend_text(GTK_COMBO_BOX(zd->widget[ii].widget),data);
 
4667
   return 1;
 
4668
}
 
4669
 
 
4670
 
 
4671
//  get combo box drop-down list entry
 
4672
 
 
4673
char * zdialog_cb_get(zdialog *zd, const char *name, int Nth)
 
4674
{
 
4675
   int      ii;
 
4676
 
 
4677
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4678
   if (zd->sentinel != zdsentinel) return 0;
 
4679
 
 
4680
   for (ii = 1; zd->widget[ii].type; ii++)                                 //  find widget
 
4681
      if (strEqu(zd->widget[ii].name,name)) break;
 
4682
   if (! zd->widget[ii].type) return 0;                                    //  not found
 
4683
   if (! strcmpv(zd->widget[ii].type,"combo","comboE",0)) return 0;        //  not combo box
 
4684
   return pvlist_get(zd->widget[ii].cblist,Nth);
 
4685
}
 
4686
 
 
4687
 
 
4688
//  delete entry by name from combo drop down list                         //  v.2.4
 
4689
 
 
4690
int zdialog_cb_delete(zdialog *zd, const char *name, const char *data)
 
4691
{
 
4692
   int      ii, nn;
 
4693
 
 
4694
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4695
   if (zd->sentinel != zdsentinel) return 0;
 
4696
 
 
4697
   for (ii = 1; zd->widget[ii].type; ii++)                                 //  find widget
 
4698
      if (strEqu(zd->widget[ii].name,name)) break;
 
4699
   if (! zd->widget[ii].type) return 0;                                    //  not found
 
4700
   if (! strcmpv(zd->widget[ii].type,"combo","comboE",0)) return 0;        //  not combo box
 
4701
 
 
4702
   nn = pvlist_find(zd->widget[ii].cblist,data);                           //  find entry by name
 
4703
   if (nn < 0) return -1;
 
4704
   pvlist_remove(zd->widget[ii].cblist,nn);                                //  remove from memory list
 
4705
   gtk_combo_box_remove_text(GTK_COMBO_BOX(zd->widget[ii].widget),nn);     //    and from widget
 
4706
   gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1);      //  set no active entry
 
4707
   return 0;
 
4708
}
 
4709
 
 
4710
 
 
4711
//  delete all entries from combo drop down list
 
4712
 
 
4713
int zdialog_cb_clear(zdialog *zd, const char *name)
 
4714
{
 
4715
   int      ii, jj, nn;
 
4716
 
 
4717
   if (! zd) return 0;                                                     //  detect destroyed dialog  v.2.2
 
4718
   if (zd->sentinel != zdsentinel) return 0;
 
4719
 
 
4720
   for (ii = 1; zd->widget[ii].type; ii++)                                 //  find widget
 
4721
      if (strEqu(zd->widget[ii].name,name)) break;
 
4722
   if (! zd->widget[ii].type) return 0;                                    //  not found
 
4723
   if (! strcmpv(zd->widget[ii].type,"combo","comboE",0)) return 0;        //  not combo box
 
4724
 
 
4725
   nn = pvlist_count(zd->widget[ii].cblist);                               //  entry count
 
4726
   for (jj = nn-1; jj >= 0; jj--) {
 
4727
      pvlist_remove(zd->widget[ii].cblist,jj);                             //  remove from memory list
 
4728
      gtk_combo_box_remove_text(GTK_COMBO_BOX(zd->widget[ii].widget),jj);  //  remove from widget
 
4729
   }
 
4730
 
 
4731
   gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1);      //  set no active entry
 
4732
   if (strEqu(zd->widget[ii].type,"comboE"))                               //  stuff entry box with nothing
 
4733
      gtk_entry_set_text(GTK_ENTRY(GTK_BIN(zd->widget[ii].widget)->child),"");
 
4734
   return 0;
 
4735
}
 
4736
 
 
4737
 
 
4738
/**************************************************************************
 
4739
    GTK misc. utility functions
 
4740
***************************************************************************/
 
4741
 
 
4742
//  output text to a popup window
 
4743
//  action: open, write, close                                             //  v.2.4
 
4744
 
 
4745
zdialog     *wtpopup_zd = 0;                                               //  v.2.18   bugfix
 
4746
GtkWidget   *wtpopup_wedit = 0;
 
4747
 
 
4748
int write_popup_text(const char *action, const char *text, int ww, int hh)
 
4749
{
 
4750
   int wtpopup_zdcompl(zdialog *zd, int zstat);
 
4751
 
 
4752
   if (strEqu(action,"open"))
 
4753
   {
 
4754
      if (wtpopup_zd) zappcrash("write_popup_text re-entered");
 
4755
 
 
4756
      wtpopup_zd = zdialog_new(text,null,"OK",null);
 
4757
      zdialog_add_widget(wtpopup_zd,"scrwin","scrwin","dialog",0,"expand");
 
4758
      zdialog_add_widget(wtpopup_zd,"edit","edit","scrwin");
 
4759
      wtpopup_wedit = zdialog_widget(wtpopup_zd,"edit");
 
4760
      zdialog_resize(wtpopup_zd,ww,hh);
 
4761
      zdialog_run(wtpopup_zd,null,wtpopup_zdcompl);
 
4762
   }
 
4763
   
 
4764
   if (strEqu(action,"write"))
 
4765
      if (wtpopup_wedit) wprintf(wtpopup_wedit," %s\n",text);
 
4766
   
 
4767
   if (strEqu(action,"close"))
 
4768
   {
 
4769
      if (wtpopup_zd) zdialog_free(wtpopup_zd);
 
4770
      wtpopup_zd = 0; 
 
4771
      wtpopup_wedit = 0;
 
4772
   }
 
4773
 
 
4774
   zmainloop();                                                            //  v.2.18
 
4775
   return 0;
 
4776
}
 
4777
 
 
4778
int wtpopup_zdcompl(zdialog *zd, int zstat)
 
4779
{
 
4780
   zdialog_free(wtpopup_zd);
 
4781
   wtpopup_zd = 0; 
 
4782
   wtpopup_wedit = 0;
 
4783
   return 0;
 
4784
}
 
4785
 
 
4786
 
 
4787
//  execute a command and show the output in a scrolling popup window
 
4788
 
 
4789
int popup_command(const char *command, int ww, int hh)
 
4790
{
 
4791
   int popup_zdcompl(zdialog*zd, int zstat);
 
4792
 
 
4793
   char        *buff;
 
4794
   zdialog     *zd;
 
4795
   GtkWidget   *wedit;
 
4796
   int         err, contx = 0;
 
4797
 
 
4798
   zd = zdialog_new(command,null,"OK",null);                               //  create dialog to show outputs
 
4799
   zdialog_add_widget(zd,"scrwin","scrwin","dialog",0,"expand");
 
4800
   zdialog_add_widget(zd,"edit","edit","scrwin");
 
4801
   wedit = zdialog_widget(zd,"edit");
 
4802
   zdialog_resize(zd,ww,hh);
 
4803
   zdialog_run(zd,null,popup_zdcompl);                                     //  show dialog with command outputs
 
4804
 
 
4805
   while ((buff = command_output(contx,command)))                          //  v.2.3
 
4806
   {
 
4807
      wprintf(wedit," %s \n",buff);                                        //  *** GTK memory leak is here ***
 
4808
      zfree(buff);
 
4809
   }
 
4810
   
 
4811
   wscroll(wedit,1);                                                       //  scroll back to first line    v.2.3
 
4812
   
 
4813
   err = command_status(contx);
 
4814
   return err;                                                             //  v.2.12
 
4815
}
 
4816
 
 
4817
int popup_zdcompl(zdialog *zd, int zstat)
 
4818
{
 
4819
   zdialog_free(zd);
 
4820
   return 0;
 
4821
}
 
4822
 
 
4823
 
 
4824
//  display message box and wait for user OK
 
4825
//  *** be careful about calling this before gtk_main() is started *** 
 
4826
 
 
4827
void zmessageACK(cchar *pMess, ... )
 
4828
{
 
4829
   GtkWidget      *dialog;
 
4830
   va_list        arglist;
 
4831
   char           message[400];
 
4832
   const char     *blanks = "                           ";
 
4833
 
 
4834
   va_start(arglist,pMess);
 
4835
   vsnprintf(message,400,pMess,arglist);
 
4836
   va_end(arglist);
 
4837
   
 
4838
   if (strlen(message) < 20) strcat(message,blanks);                       //  make box longer
 
4839
 
 
4840
   zlock();
 
4841
   dialog = gtk_message_dialog_new(null,GTK_DIALOG_MODAL,
 
4842
                    GTK_MESSAGE_INFO,GTK_BUTTONS_OK, message, null);
 
4843
   gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);          //  v.2.7.1
 
4844
   gtk_dialog_run(GTK_DIALOG(dialog));
 
4845
   gtk_widget_destroy(dialog);
 
4846
   zunlock();
 
4847
 
 
4848
   return;
 
4849
}
 
4850
 
 
4851
 
 
4852
//  log error message to STDOUT as well as message box and user OK
 
4853
 
 
4854
void zmessLogACK(cchar *pMess, ...)                                        //  v.2.9
 
4855
{
 
4856
   va_list        arglist;
 
4857
   char           message[200];
 
4858
 
 
4859
   va_start(arglist,pMess);
 
4860
   vsnprintf(message,200,pMess,arglist);
 
4861
   va_end(arglist);
 
4862
   
 
4863
   printf("%s \n",message);
 
4864
   zmessageACK(message);
 
4865
   return;
 
4866
 
4867
 
 
4868
 
 
4869
//  display message box and wait for user Yes or No response
 
4870
//  *** be careful about calling this before gtk_main() is started *** 
 
4871
//  *** uses text buttons "yes" and "no" regardless of locale ***
 
4872
 
 
4873
int zmessageYN(cchar *pMess, ... )
 
4874
{
 
4875
   GtkWidget  *dialog;
 
4876
   va_list     arglist;
 
4877
   char        message[400];
 
4878
   int         stat;
 
4879
 
 
4880
   va_start(arglist,pMess);
 
4881
   vsnprintf(message,400,pMess,arglist);
 
4882
   va_end(arglist);
 
4883
 
 
4884
   zlock();
 
4885
   dialog = gtk_message_dialog_new(null,GTK_DIALOG_MODAL,
 
4886
                 GTK_MESSAGE_QUESTION,GTK_BUTTONS_YES_NO, message, null);
 
4887
   gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);          //  v.2.7.1
 
4888
   stat = gtk_dialog_run(GTK_DIALOG(dialog));
 
4889
   gtk_widget_destroy(dialog);
 
4890
   zunlock();
 
4891
 
 
4892
   if (stat == GTK_RESPONSE_YES) return 1;
 
4893
   else return 0;
 
4894
}
 
4895
 
 
4896
 
 
4897
//  get text input from a popup dialog
 
4898
 
 
4899
const char * dialogText(const char *title, const char *inittext)
 
4900
{
 
4901
   zdialog  *zd;
 
4902
   
 
4903
   zd = zdialog_new(title,null,"OK",ZTX("cancel"),null);
 
4904
   zdialog_add_widget(zd,"frame","fred","dialog");
 
4905
   zdialog_add_widget(zd,"edit","edit","fred");
 
4906
   zdialog_stuff(zd,"edit",inittext);
 
4907
 
 
4908
   int zstat = zdialog_run(zd);
 
4909
   zdialog_destroy(zd);
 
4910
   if (zstat != 1) return 0;
 
4911
   return zdialog_get_data(zd,"edit");
 
4912
}
 
4913
 
 
4914
 
 
4915
//  File chooser dialog. 
 
4916
//
 
4917
//  Action:  "open"           select existing file
 
4918
//           "save"           select existing or new file
 
4919
//           "folder"         select existing folder
 
4920
//           "create folder"  select existing or new folder
 
4921
//
 
4922
//  buttx:   "hidden"         button toggles display of hidden files
 
4923
//           "quality"        button to set JPG file save quality
 
4924
//
 
4925
//  Memory for returned filespec should be freed via zfree()
 
4926
 
 
4927
char     JPGquality[4] = "85";                                             //  JPG file save quality (extern)
 
4928
 
 
4929
char * zgetfile(cchar *title, cchar *initfile, cchar *action, cchar *buttx)
 
4930
{
 
4931
   void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget);          //  private function    v.2.0
 
4932
 
 
4933
   GtkFileChooserAction fcact = GTK_FILE_CHOOSER_ACTION_OPEN;
 
4934
 
 
4935
   GtkWidget      *dialog;
 
4936
   GtkWidget      *pvwidget = gtk_image_new();
 
4937
   int            fcstat, err, bcode = 0, qnum, hide = 0;
 
4938
   char           *pp, *gfile, *rfile;
 
4939
   const char     *qual, *button1 = 0, *fakeit;
 
4940
   struct stat    fstat;
 
4941
   
 
4942
   zlock();
 
4943
 
 
4944
   if (strEqu(action,"open")) {
 
4945
      fcact = GTK_FILE_CHOOSER_ACTION_OPEN;
 
4946
      button1 = ZTX("open");
 
4947
   }
 
4948
 
 
4949
   if (strEqu(action,"save")) {
 
4950
      fcact = GTK_FILE_CHOOSER_ACTION_SAVE;
 
4951
      button1 = ZTX("save");
 
4952
   }
 
4953
 
 
4954
   if (strEqu(action,"folder")) {
 
4955
      fcact = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
 
4956
      button1 = ZTX("open folder");
 
4957
   }
 
4958
 
 
4959
   if (strEqu(action,"create folder")) {
 
4960
      fcact = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
 
4961
      button1 = ZTX("create folder");
 
4962
   }
 
4963
   
 
4964
   if (buttx) {
 
4965
      if (strnEqu(buttx,"hidden",6)) bcode = 103;
 
4966
      if (strEqu(buttx,"quality")) bcode = 104;
 
4967
      fakeit = ZTX("hidden");                                              //  generate text for translation
 
4968
      fakeit = ZTX("quality");
 
4969
   }
 
4970
   
 
4971
   dialog = gtk_file_chooser_dialog_new(title, null, fcact,                //  create file selection dialog
 
4972
                              button1, GTK_RESPONSE_ACCEPT, 
 
4973
                              ZTX("cancel"), GTK_RESPONSE_CANCEL, 
 
4974
                              ZTX(buttx), bcode, null);
 
4975
   gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
 
4976
 
 
4977
   if (! strstr(action,"folder")) {                                        //  connect preview maker
 
4978
      gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog),pvwidget);
 
4979
      G_SIGNAL(dialog,"update-preview",zgetfile_preview,pvwidget);
 
4980
   }
 
4981
 
 
4982
   if (initfile) {                                                         //  pre-select filespec   v.2.19
 
4983
      err = stat(initfile,&fstat);
 
4984
      if (err) {                                                           //  attempt to set folder and file name
 
4985
         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),initfile);
 
4986
         pp = (char *) strrchr(initfile,'/');
 
4987
         if (pp) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),pp+1);
 
4988
      }
 
4989
      else if (S_ISREG(fstat.st_mode))
 
4990
         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),initfile);
 
4991
      else if (S_ISDIR(fstat.st_mode))
 
4992
         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),initfile);
 
4993
   }
 
4994
 
 
4995
   if (strEqu(action,"save"))                                              //  overwrite confirmation
 
4996
      gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),1);
 
4997
 
 
4998
   gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),0);           //  default: no show hidden
 
4999
 
 
5000
   while (true)
 
5001
   {
 
5002
      fcstat = gtk_dialog_run(GTK_DIALOG(dialog));                         //  run dialog, get status button
 
5003
 
 
5004
      if (fcstat == 103) {                                                 //  show/hide hidden files
 
5005
         hide = gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(dialog));
 
5006
         hide = 1 - hide;
 
5007
         gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),hide);
 
5008
      }
 
5009
 
 
5010
      else if (fcstat == 104) {                                            //  get JPG quality parameter 
 
5011
         while (true) {
 
5012
            qual = dialogText(ZTX("JPG quality 0-100"),JPGquality);
 
5013
            if (! qual) break;                                             //  cancel = no change
 
5014
            err = convSI(qual,qnum,0,100);
 
5015
            if (err) continue;                                             //  enforce 0-100
 
5016
            snprintf(JPGquality,4,"%d",qnum);
 
5017
            break;
 
5018
         }
 
5019
      }
 
5020
 
 
5021
      else break;                                                          //  some other button 
 
5022
   }
 
5023
 
 
5024
   if (fcstat == GTK_RESPONSE_ACCEPT) 
 
5025
      gfile = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));     //  get chosen file
 
5026
   else  gfile = 0;
 
5027
 
 
5028
   gtk_widget_destroy(dialog);
 
5029
 
 
5030
   if (gfile) {                                                            //  copy into own memory
 
5031
      rfile = strdupz(gfile);
 
5032
      g_free(gfile);                                                       //  free GTK resource
 
5033
   }
 
5034
   else rfile = 0;
 
5035
   
 
5036
   zunlock();
 
5037
   return rfile;
 
5038
}
 
5039
 
 
5040
 
 
5041
//  zgetfile private function - get preview images for image files         //  v.2.0
 
5042
 
 
5043
void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget)
 
5044
{
 
5045
   GdkPixbuf   *thumbnail;
 
5046
   char        *filename;
 
5047
   struct stat statbuf;
 
5048
   int         err;
 
5049
 
 
5050
   thumbnail = 0;
 
5051
   filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(dialog));
 
5052
   err = stat(filename,&statbuf);
 
5053
   if (! err && ! S_ISDIR(statbuf.st_mode))                                //  not for directories   v.2.9
 
5054
      thumbnail = image_thumbnail(filename,128);                           //  use 128x128 pixels    v.2.9
 
5055
   g_free(filename);
 
5056
 
 
5057
   if (thumbnail) {
 
5058
      gtk_image_set_from_pixbuf(GTK_IMAGE(pvwidget),thumbnail);
 
5059
      gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),1);
 
5060
      g_object_unref(thumbnail);                                           //  v.2.5  leak
 
5061
   }
 
5062
   else
 
5063
      gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0);
 
5064
   return;
 
5065
}
 
5066
 
 
5067
 
 
5068
//  show a local or remote html file using the user's preferred browser    //  v.2.18
 
5069
 
 
5070
void showz_html(const char *url)
 
5071
{
 
5072
   char           command[500];
 
5073
   static char    prog[20];
 
5074
   static int     ftf = 1, err;
 
5075
 
 
5076
   if (ftf) {
 
5077
      ftf = 0;
 
5078
      strcpy(prog,"xdg-open");
 
5079
      err = system("xdg-open --version");
 
5080
      if (err) {
 
5081
         strcpy(prog,"firefox");
 
5082
         err = system("firefox -v");
 
5083
         if (err) *prog = 0;
 
5084
      }
 
5085
   }
 
5086
   
 
5087
   if (! *prog) {
 
5088
      zmessLogACK("no xdg-open or firefox, cannot show document");
 
5089
      return;
 
5090
   }
 
5091
   
 
5092
   snprintf(command,499,"%s %s &",prog,url);                               //  add '&'   v.2.18
 
5093
   err = system(command);
 
5094
   return;
 
5095
}
 
5096
 
 
5097
 
 
5098
//  display message box indefinitely, until caller kills it                v.2.6
 
5099
 
 
5100
GtkWidget * zmessage_post(cchar *pMess, ... )
 
5101
{
 
5102
   GtkWidget      *dialog, *label1, *label2, *label3;
 
5103
   va_list        arglist;
 
5104
   char           message1[400];
 
5105
   char           message2[400];
 
5106
   const char     *b20 = "                    ";
 
5107
   int            cc1, cc2;
 
5108
 
 
5109
   va_start(arglist,pMess);
 
5110
   vsnprintf(message1,400,pMess,arglist);
 
5111
   va_end(arglist);
 
5112
   
 
5113
   cc1 = strlen(message1);
 
5114
   if (cc1 > 20) strcpy(message2,message1);
 
5115
   else {
 
5116
      cc2 = (22 - cc1) / 2;
 
5117
      strncpy(message2,b20,cc2);
 
5118
      strcpy(message2+cc2,message1);
 
5119
      strncpy(message2+cc2+cc1,b20,cc2);
 
5120
      message2[cc2+cc1+cc2] = 0;
 
5121
   }
 
5122
   
 
5123
   zlock();
 
5124
   dialog = gtk_dialog_new_with_buttons("",null,(GtkDialogFlags) 0,null);
 
5125
   label1 = gtk_label_new("  ");
 
5126
   label2 = gtk_label_new(message2);
 
5127
   label3 = gtk_label_new("  ");
 
5128
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),label1);
 
5129
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),label2);
 
5130
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),label3);
 
5131
   gtk_widget_show_all(dialog);
 
5132
   zunlock();
 
5133
 
 
5134
   return dialog;
 
5135
}
 
5136
 
 
5137
void zmessage_kill(GtkWidget *dialog)
 
5138
{
 
5139
   zlock();
 
5140
   gtk_widget_destroy(dialog);
 
5141
   zunlock();
 
5142
   return;
 
5143
}
 
5144
 
 
5145
 
 
5146
//  connect a user callback function to a window drag-drop event
 
5147
 
 
5148
void drag_drop_connect(GtkWidget *window, drag_drop_func *ufunc)           //  v.2.19
 
5149
{
 
5150
   int  drag_drop_connect2(GtkWidget *, void *, int, int, void *, int, int, void *);
 
5151
   char     string[] = "STRING";
 
5152
   GtkTargetEntry  file_drop_target = { string, 0, 0 };
 
5153
 
 
5154
   gtk_drag_dest_set(window, GTK_DEST_DEFAULT_ALL, &file_drop_target, 1, GDK_ACTION_COPY);
 
5155
   G_SIGNAL(window, "drag-data-received", drag_drop_connect2, ufunc)
 
5156
   return;
 
5157
}
 
5158
 
 
5159
 
 
5160
//  private function 
 
5161
//  get dropped file, clean escapes, pass to user function
 
5162
//  passed filespec is subject for zfree()
 
5163
 
 
5164
int drag_drop_connect2(GtkWidget *, void *, int mpx, int mpy, void *sdata, int, int, void *ufunc)
 
5165
{
 
5166
   char  * drag_drop_unescape(const char *escaped_string);
 
5167
   drag_drop_func  *ufunc2;
 
5168
 
 
5169
   char     *text, *text2, *file, *file2;
 
5170
   int      cc;
 
5171
   
 
5172
   text = (char *) ((GtkSelectionData *) sdata)->data;
 
5173
   ufunc2 = (drag_drop_func *) ufunc;
 
5174
 
 
5175
   if (strstr(text,"file://"))                                             //  text is a filespec
 
5176
   {
 
5177
      file = strdupz(text+7);                                              //  get rid of junk added by GTK
 
5178
      cc = strlen(file);
 
5179
      while (file[cc-1] < ' ') cc--;
 
5180
      file[cc] = 0;
 
5181
      file2 = drag_drop_unescape(file);                                    //  clean %xx escapes from Nautilus
 
5182
      zfree(file);
 
5183
      ufunc2(mpx,mpy,file2);                                               //  pass file to user function
 
5184
   }
 
5185
   
 
5186
   else 
 
5187
   {
 
5188
      text2 = strdupz(text);
 
5189
      ufunc2(mpx,mpy,text2);
 
5190
   }
 
5191
   
 
5192
   return 1;
 
5193
}
 
5194
 
 
5195
//  private function
 
5196
//  Clean %xx escapes from strange Nautilus drag-drop file names
 
5197
 
 
5198
char * drag_drop_unescape(const char *inp)
 
5199
{
 
5200
   int  drag_drop_convhex(char ch);
 
5201
 
 
5202
   char     inch, *out, *outp;
 
5203
   int      nib1, nib2;
 
5204
   
 
5205
   out = zmalloc(strlen(inp) + 1);
 
5206
   outp = out;
 
5207
   
 
5208
   while ((inch = *inp++))
 
5209
   {
 
5210
      if (inch == '%')
 
5211
      {
 
5212
         nib1 = drag_drop_convhex(*inp++);
 
5213
         nib2 = drag_drop_convhex(*inp++);
 
5214
         *outp++ = nib1 << 4 | nib2;
 
5215
      }
 
5216
      else *outp++ = inch;
 
5217
   }
 
5218
   
 
5219
   *outp = 0;
 
5220
   return out;
 
5221
}
 
5222
 
 
5223
//  private function - convert character 0-F to number 0-15
 
5224
 
 
5225
int drag_drop_convhex(char ch)
 
5226
{
 
5227
   if (ch >= '0' && ch <= '9') return  ch - '0';
 
5228
   if (ch >= 'A' && ch <= 'F') return  ch - 'A' + 10;
 
5229
   if (ch >= 'a' && ch <= 'f') return  ch - 'a' + 10;
 
5230
   return ch;
 
5231
}
 
5232
 
 
5233
 
 
5234
/**************************************************************************
 
5235
   GDK graphics utilities
 
5236
***************************************************************************/
 
5237
 
 
5238
/**************************************************************************  
 
5239
 
 
5240
     GdkPixbuf * gdk_pixbuf_rotate(GdkPixbuf *pixbuf, double angle, int acolor)
 
5241
 
 
5242
     Rotate a pixbuf through an arbitrary angle (degrees).
 
5243
 
 
5244
     The returned image has the same size as the original, but the
 
5245
     pixbuf envelope is increased to accomodate the rotated original
 
5246
     (e.g. a 100x100 pixbuf rotated 45 deg. needs a 142x142 pixbuf).
 
5247
 
 
5248
     Pixels added around the rotated image have all RGB values = acolor.   //  v.2.17
 
5249
     Angle is in degrees. Positive direction is clockwise.
 
5250
     Pixbuf must have 8 bits per channel and 3 or 4 channels.
 
5251
     Loss of resolution is < 1 pixel.
 
5252
     Speed is about 2.4 million pixels/sec. for a 2 GHz CPU.
 
5253
     
 
5254
     NULL is returned if the function fails for one of the following:
 
5255
         - pixbuf not 8 bits/channel or < 3 channels
 
5256
         - unable to create output pixbuf (lack of memory?)
 
5257
    
 
5258
     Algorithm:
 
5259
         create output pixbuf big enough for rotated input pixbuf
 
5260
         loop all output pixels
 
5261
            get next output pixel (px2,py2)
 
5262
            compute (R,theta) from center of pixbuf
 
5263
            rotate theta by -angle
 
5264
            (R,theta) is now within the closest input pixel
 
5265
            convert to input pixel (px1,py1)
 
5266
            if outside of pixmap
 
5267
               output pixel = black
 
5268
               continue
 
5269
            for 4 input pixels based at (px0,py0) = (int(px1),int(py1))
 
5270
               compute overlap (0 to 1) with (px1,py1)
 
5271
               sum RGB values * overlap
 
5272
            output aggregate RGB to pixel (px2,py2)
 
5273
 
 
5274
***************************************************************************/
 
5275
 
 
5276
GdkPixbuf * gdk_pixbuf_rotate(GdkPixbuf *pixbuf1, double angle, int acolor)
 
5277
{
 
5278
   typedef unsigned char  *pixel;                                          //  3 RGB values, 0-255 each
 
5279
 
 
5280
   GdkPixbuf      *pixbuf2;
 
5281
   GdkColorspace  color;
 
5282
 
 
5283
   int      nch, nbits, alpha;
 
5284
   int      pbW1, pbH1, pbR1, pbW2, pbH2, pbR2;
 
5285
   int      px2, py2, px0, py0;
 
5286
   pixel    ppix1, ppix2, pix0, pix1, pix2, pix3;
 
5287
   double   rx1, ry1, rx2, ry2, R, theta, px1, py1;
 
5288
   double   f0, f1, f2, f3, red, green, blue;
 
5289
   double   pi = 3.141592654;
 
5290
 
 
5291
   nch = gdk_pixbuf_get_n_channels(pixbuf1);
 
5292
   nbits = gdk_pixbuf_get_bits_per_sample(pixbuf1);
 
5293
   if (nch < 3) return 0;                                                  //  must have 3+ channels (colors)
 
5294
   if (nbits != 8) return 0;                                               //  must be 8 bits per channel
 
5295
 
 
5296
   color = gdk_pixbuf_get_colorspace(pixbuf1);                             //  get input pixbuf1 attributes
 
5297
   alpha = gdk_pixbuf_get_has_alpha(pixbuf1);
 
5298
   pbW1 = gdk_pixbuf_get_width(pixbuf1);
 
5299
   pbH1 = gdk_pixbuf_get_height(pixbuf1);
 
5300
   pbR1 = gdk_pixbuf_get_rowstride(pixbuf1);
 
5301
 
 
5302
   while (angle < -180) angle += 360;                                      //  normalize, -180 to +180
 
5303
   while (angle > 180) angle -= 360;
 
5304
   angle = angle * pi / 180;                                               //  radians, -pi to +pi
 
5305
   
 
5306
   if (fabs(angle) < 0.001) {                                              //  bugfix 0.01 >> 0.001   v.2.1
 
5307
      pixbuf2 = gdk_pixbuf_copy(pixbuf1);                                  //  angle is zero within my precision
 
5308
      return pixbuf2;
 
5309
   }
 
5310
 
 
5311
   pbW2 = int(pbW1*fabs(cos(angle)) + pbH1*fabs(sin(angle)));              //  rectangle containing rotated image
 
5312
   pbH2 = int(pbW1*fabs(sin(angle)) + pbH1*fabs(cos(angle)));
 
5313
 
 
5314
   pixbuf2 = gdk_pixbuf_new(color,alpha,nbits,pbW2,pbH2);                  //  create output pixbuf2
 
5315
   if (! pixbuf2) return 0;
 
5316
   pbR2 = gdk_pixbuf_get_rowstride(pixbuf2);
 
5317
   
 
5318
   ppix1 = gdk_pixbuf_get_pixels(pixbuf1);                                 //  input pixel array
 
5319
   ppix2 = gdk_pixbuf_get_pixels(pixbuf2);                                 //  output pixel array
 
5320
   
 
5321
   for (py2 = 0; py2 < pbH2; py2++)                                        //  loop through output pixels
 
5322
   for (px2 = 0; px2 < pbW2; px2++)                                        //  outer loop y   v.2.11
 
5323
   {
 
5324
      rx2 = px2 - 0.5 * pbW2;                                              //  (rx2,ry2) = center of pixel
 
5325
      ry2 = py2 - 0.5 * pbH2;
 
5326
      R = sqrt(rx2*rx2 + ry2*ry2);                                         //  convert to (R,theta)
 
5327
      if (R < 0.1) theta = 0;
 
5328
      else theta = qarcsine(ry2 / R);                                      //  quick arc sine
 
5329
      if (rx2 < 0) {
 
5330
         if (theta < 0) theta = - pi - theta;                              //  adjust for quandrant
 
5331
         else theta = pi - theta;
 
5332
      }
 
5333
 
 
5334
      theta = theta - angle;                                               //  rotate theta backwards
 
5335
      if (theta > pi) theta -= 2 * pi;                                     //  range -pi to +pi
 
5336
      if (theta < -pi) theta += 2 * pi;
 
5337
 
 
5338
      rx1 = R * qcosine(theta);                                            //  quick cosine, sine
 
5339
      ry1 = R * qsine(theta);
 
5340
      px1 = rx1 + 0.5 * pbW1;                                              //  (px1,py1) = corresponding
 
5341
      py1 = ry1 + 0.5 * pbH1;                                              //    point within input pixels
 
5342
 
 
5343
      px0 = int(px1);                                                      //  pixel containing (px1,py1)
 
5344
      py0 = int(py1);
 
5345
      
 
5346
      if (px1 < 0 || px0 >= pbW1-1 || py1 < 0 || py0 >= pbH1-1) {          //  if outside input pixel array
 
5347
         pix2 = ppix2 + py2 * pbR2 + px2 * nch;                            //    output is acolor    v.2.17
 
5348
         pix2[0] = pix2[1] = pix2[2] = acolor;
 
5349
         continue;
 
5350
      }
 
5351
 
 
5352
      pix0 = ppix1 + py0 * pbR1 + px0 * nch;                               //  4 input pixels based at (px0,py0)
 
5353
      pix1 = pix0 + pbR1;
 
5354
      pix2 = pix0 + nch;
 
5355
      pix3 = pix0 + pbR1 + nch;
 
5356
 
 
5357
      f0 = (px0+1 - px1) * (py0+1 - py1);                                  //  overlap of (px1,py1)
 
5358
      f1 = (px0+1 - px1) * (py1 - py0);                                    //    in each of the 4 pixels
 
5359
      f2 = (px1 - px0) * (py0+1 - py1);
 
5360
      f3 = (px1 - px0) * (py1 - py0);
 
5361
   
 
5362
      red =   f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0];   //  sum the weighted inputs
 
5363
      green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1];
 
5364
      blue =  f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2];
 
5365
      
 
5366
      if (red == acolor && green == acolor && blue == acolor) {            //  avoid acolor in image     v.2.17
 
5367
         if (blue == 0) blue = 1;
 
5368
         else blue--;
 
5369
      }
 
5370
      
 
5371
      pix2 = ppix2 + py2 * pbR2 + px2 * nch;                               //  output pixel
 
5372
      pix2[0] = int(red);
 
5373
      pix2[1] = int(green);
 
5374
      pix2[2] = int(blue);
 
5375
   }
 
5376
      
 
5377
   return pixbuf2;
 
5378
}
 
5379
 
 
5380
 
 
5381
/**************************************************************************
 
5382
 
 
5383
   functions for navigtion of image files in a directory                   //  overhauled  v.2.3
 
5384
      - get first or last image, previous or next image
 
5385
      - create a window of thumbnail images (index window)
 
5386
      - use thumbnail window to navigate and select images
 
5387
 
 
5388
**************************************************************************/
 
5389
 
 
5390
namespace image_navi {
 
5391
 
 
5392
   #define flimit 10000                                                    //  max image files in one directory
 
5393
   #define indexfont "sans 8"                                              //  font for thumbnail labels
 
5394
   #define imagefiles ".jpeg .jpg .png .tif .tiff .bmp .gif .svg .xpm"     //  supported file types
 
5395
 
 
5396
   #define nodither GDK_RGB_DITHER_NONE,0,0
 
5397
   #define interp GDK_INTERP_BILINEAR
 
5398
   #define colorspace GDK_COLORSPACE_RGB
 
5399
 
 
5400
   #define     thumbfilesize 256                                           //  thumbnail file image size
 
5401
   #define     thumbxx 5                                                   //  thumbx array size
 
5402
   int         thumbx[5] = { 256, 180, 128, 90, 64 };                      //  thumbnail image step sizes   v.2.9
 
5403
 
 
5404
   char        dirkx[maxfcc];                                              //  image directory
 
5405
   int         nfiles = 0;                                                 //  image file count
 
5406
   char        **flist = 0;                                                //  image file list
 
5407
 
 
5408
   image_cbfunc   *userfunc;                                               //  callback function for clicked image
 
5409
 
 
5410
   GtkWidget   *windx = 0, *vboxx, *dwindx;                                //  thumbnail index and drawing window
 
5411
   GdkGC       *gdkgc = 0;                                                 //  graphics context
 
5412
   GError      **gerror = 0;
 
5413
 
 
5414
   char        *filex = 0;                                                 //  index window anchor file
 
5415
   int         xwinW = 1000, xwinH = 700;                                  //  index window size (dwindx)
 
5416
   int         thumbsize = thumbfilesize;                                  //  thumbnail image <= thumbnail file
 
5417
   int         thumbW, thumbH;                                             //  index window thumbnail cell size
 
5418
   int         xrows, xcols;                                               //  index window thumbnail rows, cols
 
5419
   int         xmargW, xmargH;                                             //  cell margin from left and top edge
 
5420
 
 
5421
   //  private functions
 
5422
   void  windx_paint();                                                    //  index window paint function
 
5423
   void  windx_destroy();                                                  //  index window destroy event function
 
5424
   void  draw_xtext(GtkWidget *win, char *text, int x, int y);             //  draw text in index window
 
5425
   void  menufuncx(GtkWidget *win, const char *menu);                      //  function for index window buttons
 
5426
   void  mouse_xevent(GtkWidget *, GdkEventButton *, void *);              //  index window mouse event function
 
5427
        int   KBxpress(GtkWidget *, GdkEventKey *, void *);                     //  index window key press event func.
 
5428
        int   KBxrelease(GtkWidget *, GdkEventKey *, void *);                   //  index window key release event
 
5429
   char * image_navigate(cchar *filez, cchar *action, int Nth = 0);        //  image file list setup and navigate
 
5430
   int image_fcomp(const char *file1, const char *file2);                  //  file name compare (special sort)
 
5431
}
 
5432
 
 
5433
using namespace image_navi;
 
5434
 
 
5435
 
 
5436
/**************************************************************************
 
5437
 
 
5438
   public function to create/update thumbnail window (index window)                 v.2.3
 
5439
 
 
5440
   Make window of thumbnails starting with filez (anchor)
 
5441
   Handle window buttons (up row, down page, open file, etc.)
 
5442
   Call ufunc() when thumbnail image is clicked
 
5443
   
 
5444
   filez:   anchor file or directory of image files
 
5445
 
 
5446
   action:  init:    get internal list of image files and subdirectories
 
5447
            initF:   filez contains a list of image files to use                    v.2.4
 
5448
            find     return filez if in current list, else null
 
5449
            prev:    return filez Nth previous file
 
5450
            next:    return filez Nth next file
 
5451
            first:   return first file in list (may be a subdirectory)
 
5452
            last:    return Nth last file in list
 
5453
            paint1:  create or refresh thumbnail window, anchor = filez
 
5454
            paint2:  refresh thumbnail window if present, anchor = filez
 
5455
 
 
5456
   Nth:     1 means filez pevious, filez next, or last file in list
 
5457
            greater values mean correspondingly greater offsets
 
5458
 
 
5459
   void ufunc(char *filez)
 
5460
      - receives filename (path) of selected thumbnail
 
5461
      - filez belongs to caller and is a subject for zfree()
 
5462
   
 
5463
   Returned values:
 
5464
      For init, initF, paint1, paint2: null
 
5465
      For find, prev, next, first, last: filespec or null if not found
 
5466
      The returned file belongs to caller and is subject for zfree().
 
5467
 
 
5468
***************************************************************************/
 
5469
 
 
5470
char * image_xthumbs(cchar *filez, cchar *action, int Nth, image_cbfunc ufunc)
 
5471
{
 
5472
   GtkWidget      *tbarx;
 
5473
 
 
5474
   if (ufunc) userfunc = ufunc;                                            //  save callback function
 
5475
   
 
5476
   if (strstr("init initF find prev next first last",action))
 
5477
      return image_navigate(filez,action,Nth);                             //  create or navigate image file list
 
5478
      
 
5479
   if (filez) strdupz(filez,filex);                                        //  set new anchor file  v.2.4
 
5480
 
 
5481
   if (strEqu(action,"paint2") && ! windx) return 0;                       //  refresh, but window not active
 
5482
 
 
5483
   if (windx) {                                                            //  refresh existing index window
 
5484
      windx_paint();                                                       //  repaint
 
5485
      gtk_window_present(GTK_WINDOW(windx));                               //  bring index window to top
 
5486
      return 0;
 
5487
   }
 
5488
   
 
5489
   windx = gtk_window_new(GTK_WINDOW_TOPLEVEL);                            //  create index window
 
5490
   gtk_window_set_position(GTK_WINDOW(windx),GTK_WIN_POS_CENTER);
 
5491
   gtk_window_set_default_size(GTK_WINDOW(windx),xwinW,xwinH+56);          //  (+ toolbar size)
 
5492
 
 
5493
   vboxx = gtk_vbox_new(0,0);                                              //  vertical packing box
 
5494
   gtk_container_add(GTK_CONTAINER(windx),vboxx);                          //  add to main window
 
5495
 
 
5496
   tbarx = create_toolbar(vboxx,24);                                       //  add toolbar and buttons 
 
5497
 
 
5498
   add_toolbar_button(tbarx, ZTX("bigger"), ZTX("increase thumbnail size"), "bigger.png", menufuncx);
 
5499
   add_toolbar_button(tbarx, ZTX("smaller"), ZTX("reduce thumbnail size"), "smaller.png", menufuncx);
 
5500
   add_toolbar_button(tbarx, ZTX("prev row"), ZTX("previous row"), "prev-row.png", menufuncx);
 
5501
   add_toolbar_button(tbarx, ZTX("next row"), ZTX("next row"), "next-row.png", menufuncx);
 
5502
   add_toolbar_button(tbarx, ZTX("prev page"), ZTX("previous page"), "prev-page.png", menufuncx);
 
5503
   add_toolbar_button(tbarx, ZTX("next page"), ZTX("next page"), "next-page.png", menufuncx);
 
5504
   add_toolbar_button(tbarx, ZTX("first page"), ZTX("jump to first file"), "first-page.png", menufuncx);
 
5505
   add_toolbar_button(tbarx, ZTX("last page"), ZTX("jump to last file"), "last-page.png", menufuncx);
 
5506
   add_toolbar_button(tbarx, ZTX("file"), ZTX("jump to specific file"), "file.png", menufuncx);
 
5507
   add_toolbar_button(tbarx, ZTX("folder"), ZTX("jump to new directory"), "folder.png", menufuncx);
 
5508
   add_toolbar_button(tbarx, ZTX("close"), ZTX("close thumbnail window"), "gtk-close", menufuncx);
 
5509
 
 
5510
   dwindx = gtk_drawing_area_new();                                        //  add drawing window
 
5511
   gtk_container_add(GTK_CONTAINER(vboxx),dwindx);                         //  add to main window
 
5512
 
 
5513
   gtk_widget_show_all(windx);                                             //  show all widgets (will paint)
 
5514
 
 
5515
   gdkgc = gdk_gc_new(dwindx->window);                                     //  initz. graphics context
 
5516
 
 
5517
   G_SIGNAL(windx,"destroy",windx_destroy,0)                               //  connect window events
 
5518
   G_SIGNAL(dwindx,"expose-event",windx_paint,0)
 
5519
   gtk_widget_add_events(dwindx,GDK_BUTTON_PRESS_MASK);                    //  connect mouse events
 
5520
   G_SIGNAL(dwindx,"button-press-event",mouse_xevent,0)
 
5521
   G_SIGNAL(windx,"key-press-event",KBxpress,0)                                 //  connect KB events
 
5522
   G_SIGNAL(windx,"key-release-event",KBxrelease,0)
 
5523
 
 
5524
   return 0;
 
5525
}
 
5526
 
 
5527
 
 
5528
//  public function
 
5529
//  get current image position and total count
 
5530
 
 
5531
void image_position(const char *filez, int &posn, int &count)
 
5532
{
 
5533
   int      ii, nn;
 
5534
 
 
5535
   for (ii = nn = 0; ii < nfiles; ii++)                                    //  search for filez in file list
 
5536
   {
 
5537
      if (flist[ii][0] == '!') nn++;                                       //  directories are marked
 
5538
      if (strcmp(filez,flist[ii]) == 0) break;
 
5539
   }
 
5540
   
 
5541
   posn = ii + 1 - nn;                                                     //  return position, 1 - max
 
5542
   count = nfiles - nn;                                                    //  return total count
 
5543
   return;
 
5544
}
 
5545
 
 
5546
 
 
5547
//  public function
 
5548
//  determine if a file is a directory or a supported image file type            v.2.3
 
5549
//  return: 0 = error, 1 = directory, 2 = image file, 3 = other
 
5550
 
 
5551
int image_file_type(const char *file)
 
5552
{
 
5553
   int            err, cc;
 
5554
   const char     *pp;
 
5555
   struct stat    statbuf;
 
5556
 
 
5557
   if (! file) return 0;
 
5558
   err = stat(file,&statbuf);
 
5559
   if (err) return 0;
 
5560
 
 
5561
   if (S_ISDIR(statbuf.st_mode)) {                                         //  directory
 
5562
      cc = strlen(file);
 
5563
      if (cc > 12) {
 
5564
         pp = file + cc - 12;
 
5565
         if (strEqu(pp,"/.thumbnails")) return 3;                          //  .thumbnails
 
5566
      }
 
5567
      return 1;
 
5568
   }
 
5569
 
 
5570
   if (S_ISREG(statbuf.st_mode)) {                                         //  reg. file
 
5571
      pp = strrchr(file,'.');
 
5572
      if (! pp) return 3;
 
5573
      pp = strcasestr(imagefiles,pp);                                      //  supported image type
 
5574
      if (pp) return 2;
 
5575
   }
 
5576
   
 
5577
   return 3;
 
5578
}
 
5579
 
 
5580
 
 
5581
//  Public function
 
5582
//  Get thumbnail image for given file, from .thumbnails directory if found.           v.2.3
 
5583
//  Add missing thumbnail to cache if .thumbnails directory is present.
 
5584
//  Returned thumbnail belongs to caller: g_object_unref() is necessary.
 
5585
 
 
5586
GdkPixbuf * image_thumbnail(const char *fpath, int size)
 
5587
{
 
5588
   char              bpath[maxfcc+20], *bfile;
 
5589
   const char        *pfile;                                               //  v.2.15
 
5590
   GdkPixbuf         *thumbpxb, *temppxb;
 
5591
   int               err, sizew, sizeh;
 
5592
   struct stat       statf, statb;
 
5593
 
 
5594
   if (! size) size = thumbfilesize;                                       //  default max. size   v.2.9
 
5595
 
 
5596
   err = stat(fpath,&statf);
 
5597
   if (err) return 0;
 
5598
   
 
5599
   if (S_ISDIR(statf.st_mode)) {                                           //  if directory, return folder image
 
5600
      *bpath = 0;
 
5601
      strncatv(bpath,maxfcc,zicondir,"/folder256.png",null);
 
5602
      thumbpxb = gdk_pixbuf_new_from_file_at_size(bpath,size,size,gerror);
 
5603
      return thumbpxb;
 
5604
   }
 
5605
   
 
5606
   pfile = strrchr(fpath,'/');
 
5607
   if (! pfile) return 0;
 
5608
   pfile++;                                                                //  file name part
 
5609
 
 
5610
   strcpy(bpath,fpath);                                                    //  construct thumbnail path:
 
5611
   bfile = bpath + (pfile - fpath);                                        //  /directory/.thumbnails/filename.png
 
5612
   strcpy(bfile,".thumbnails/");                                           //  use filename instead of hash
 
5613
   bfile += 12;
 
5614
   strcpy(bfile,pfile);
 
5615
   strcat(bfile,".png");
 
5616
 
 
5617
   sizew = sizeh = thumbfilesize;                                          //  use max. size
 
5618
   thumbpxb = gdk_pixbuf_new_from_file_at_size(bpath,sizew,sizeh,gerror);  //  get thumbnail 
 
5619
   if (thumbpxb) {
 
5620
      err = stat(bpath,&statb);                                            //  found, compare date to image date
 
5621
      if (err || (statb.st_mtime < statf.st_mtime)) {
 
5622
         g_object_unref(thumbpxb);                                         //  stale - regenerate
 
5623
         thumbpxb = 0;
 
5624
      }
 
5625
   }
 
5626
 
 
5627
   if (! thumbpxb) {                                                       //  generate thumbnail from image file
 
5628
      thumbpxb = gdk_pixbuf_new_from_file_at_size(fpath,sizew,sizeh,gerror);
 
5629
      if (! thumbpxb) return 0;                                            //  hopeless
 
5630
      gdk_pixbuf_save(thumbpxb,bpath,"png",gerror,null);                   //  save to thumbnail cache (if present)
 
5631
   }
 
5632
 
 
5633
   if (size == thumbfilesize) return thumbpxb;                             //  return thumbnail if right size
 
5634
 
 
5635
   sizew = gdk_pixbuf_get_width(thumbpxb) * size / thumbfilesize;          //  rescale to right size
 
5636
   sizeh = gdk_pixbuf_get_height(thumbpxb) * size / thumbfilesize;
 
5637
   temppxb = gdk_pixbuf_scale_simple(thumbpxb,sizew,sizeh,interp);
 
5638
   g_object_unref(thumbpxb);
 
5639
   return temppxb;
 
5640
}
 
5641
 
 
5642
 
 
5643
//  private function
 
5644
//  paint index window - draw all thumbnail images that can fit
 
5645
 
 
5646
void image_navi::windx_paint()
 
5647
{
 
5648
   GdkPixbuf      *pxbT;
 
5649
   int            x, y, row, col;
 
5650
   char           *fileC, *fileN, *pp, *fname;
 
5651
   char           wintitle[200];
 
5652
 
 
5653
   snprintf(wintitle,199,"%s  %d files",dirkx,nfiles);                     //  v.2.9
 
5654
   gtk_window_set_title(GTK_WINDOW(windx),wintitle);
 
5655
   gdk_window_clear(dwindx->window);
 
5656
 
 
5657
   xwinW = dwindx->allocation.width;                                       //  curr. index window size
 
5658
   xwinH = dwindx->allocation.height;
 
5659
 
 
5660
   thumbW = thumbsize + 10;                                                //  thumbnail cell size
 
5661
   thumbH = thumbsize + 30;
 
5662
 
 
5663
   xmargW = xmargH = 5;                                                    //  edge margins
 
5664
   
 
5665
   xrows = int(0.2 + 1.0 * xwinH / thumbH);                                //  get thumbnail rows and cols that
 
5666
   xcols = int(0.3 + 1.0 * xwinW / thumbW);                                //    (almost) fit in window 
 
5667
   if (xrows < 1) xrows = 1;
 
5668
   if (xcols < 1) xcols = 1;
 
5669
   
 
5670
   if (! filex) return;
 
5671
   fileC = strdupz(filex);                                                 //  start with anchor file
 
5672
 
 
5673
   for (row = 0; row < xrows; row++)                                       //  draw thumbnails
 
5674
   for (col = 0; col < xcols; col++)
 
5675
   {
 
5676
      x = col * thumbW + xmargW;
 
5677
      y = row * thumbH + xmargH;
 
5678
 
 
5679
      pp = (char *) strrchr(fileC,'/');                                    //  draw file name 
 
5680
      if (pp) fname = pp + 1;
 
5681
      else fname = fileC;
 
5682
      draw_xtext(dwindx,fname,x,y);
 
5683
 
 
5684
      pxbT = image_thumbnail(fileC,thumbsize);                             //  get thumbnail, draw it
 
5685
      if (pxbT) {
 
5686
         gdk_draw_pixbuf(dwindx->window,0,pxbT,0,0,x,y+20,-1,-1,nodither); 
 
5687
         g_object_unref(pxbT);
 
5688
      }
 
5689
 
 
5690
      fileN = image_navigate(fileC,"next",1);                              //  get next image file
 
5691
      zfree(fileC);
 
5692
      if (! fileN) return;
 
5693
      fileC = fileN;
 
5694
   }
 
5695
 
 
5696
   return;
 
5697
}
 
5698
 
 
5699
 
 
5700
//  private function
 
5701
//  write text for thumbnail limited by width of thumbnail
 
5702
 
 
5703
void image_navi::draw_xtext(GtkWidget *win, char *text, int x, int y)
 
5704
{
 
5705
   static PangoFontDescription   *pfont = 0;
 
5706
   static PangoLayout            *playout = 0;
 
5707
 
 
5708
   int            ww, hh, cc;
 
5709
   char           text2[100];
 
5710
 
 
5711
   if (! pfont) {
 
5712
      pfont = pango_font_description_from_string(indexfont);               //  first call, get font sizing poop
 
5713
      playout = gtk_widget_create_pango_layout(win,0);
 
5714
      pango_layout_set_font_description(playout,pfont);
 
5715
   }
 
5716
 
 
5717
   for (cc = 50; cc > 7; cc--)                                             //  allow up to 50 graphic chars.
 
5718
   {                                                                       //    and reduce until fits      v.2.3
 
5719
      cc = utf8substring(text2,text,0,cc);                                 //  get substring up to cc chars.
 
5720
      pango_layout_set_text(playout,text2,-1);                             //  compute layout
 
5721
      pango_layout_get_pixel_size(playout,&ww,&hh);                        //  pixel width of layout
 
5722
      if (ww < thumbsize+5) break;                                         //  stop if it fits thumbnail size
 
5723
   }
 
5724
 
 
5725
   gdk_draw_layout(win->window,gdkgc,x,y,playout);
 
5726
   return;
 
5727
}
 
5728
 
 
5729
 
 
5730
//  private function
 
5731
//  index window destroy event - track if window is active or not
 
5732
 
 
5733
void image_navi::windx_destroy()
 
5734
{
 
5735
   windx = 0;                                                              //  no window
 
5736
   if (filex) zfree(filex);                                                //  no anchor file
 
5737
   filex = 0;
 
5738
   return;
 
5739
}
 
5740
 
 
5741
 
 
5742
//  private function - menu function for index window
 
5743
//    - scroll window as requested
 
5744
//    - jump to new file or folder as requested
 
5745
 
 
5746
void image_navi::menufuncx(GtkWidget *win, const char *menu)
 
5747
{
 
5748
   char        *filez;
 
5749
   cchar       *action = 0;
 
5750
   int         ii, count = 0;
 
5751
   
 
5752
   if (strEqu(menu,ZTX("bigger")))  {                                      //  next bigger thumbnail size
 
5753
      for (ii = 0; ii < thumbxx; ii++) 
 
5754
            if (thumbsize == thumbx[ii]) break;
 
5755
      if (ii == 0) return;
 
5756
      thumbsize = thumbx[ii-1];
 
5757
      windx_paint();
 
5758
      return;
 
5759
   }
 
5760
 
 
5761
   if (strEqu(menu,ZTX("smaller")))  {                                     //  next smaller
 
5762
      for (ii = 0; ii < thumbxx; ii++) 
 
5763
            if (thumbsize == thumbx[ii]) break;
 
5764
      if (ii == thumbxx-1) return;
 
5765
      thumbsize = thumbx[ii+1];
 
5766
      windx_paint();
 
5767
      return;
 
5768
   }
 
5769
 
 
5770
   if (strEqu(menu,ZTX("file"))) {                                         //  go to specific file
 
5771
      filez = zgetfile(ZTX("select new file"),filex,"open");               //  file chooser dialog
 
5772
      if (filez) {
 
5773
         image_navigate(filez,"init");                                     //  get new file list
 
5774
         windx_paint();
 
5775
      }
 
5776
      return;
 
5777
   }
 
5778
 
 
5779
   if (strEqu(menu,ZTX("folder"))) {                                       //  initz. all files in folder
 
5780
      filez = zgetfile(ZTX("select new folder"),filex,"folder");           //  file chooser dialog (folder)
 
5781
      if (filez) {
 
5782
         image_navigate(filez,"init");                                     //  get new file list
 
5783
         windx_paint();
 
5784
      }
 
5785
      return;
 
5786
   }
 
5787
   
 
5788
   if (strEqu(menu,ZTX("close"))) {
 
5789
      gtk_widget_destroy(windx);                                           //  close thumbnail window
 
5790
      return;
 
5791
   }
 
5792
 
 
5793
   if (! filex) return;                                                    //  no anchor file
 
5794
 
 
5795
   if (strEqu(menu,ZTX("prev row"))) {
 
5796
      action = "prev";
 
5797
      count = xcols;
 
5798
   }
 
5799
 
 
5800
   if (strEqu(menu,ZTX("next row"))) {
 
5801
      action = "next";
 
5802
      count = xcols;
 
5803
   }
 
5804
 
 
5805
   if (strEqu(menu,ZTX("prev page"))) {
 
5806
      action = "prev";
 
5807
      count = xcols * xrows;
 
5808
   }
 
5809
 
 
5810
   if (strEqu(menu,ZTX("next page"))) {
 
5811
      action = "next";
 
5812
      count = xcols * xrows;
 
5813
   }
 
5814
 
 
5815
   if (strEqu(menu,ZTX("first page"))) {
 
5816
      action = "first";
 
5817
      count = 0;
 
5818
   }
 
5819
 
 
5820
   if (strEqu(menu,ZTX("last page"))) {
 
5821
      action = "last";
 
5822
      count = xcols * xrows;                                               //  last + prior fitting in window
 
5823
   }
 
5824
   
 
5825
   filez = image_navigate(filex,action,count);                             //  get file starting prev/next row/page
 
5826
   if (! filez) return;
 
5827
   zfree(filex);
 
5828
   filex = filez;                                                          //  new anchor file
 
5829
   windx_paint();                                                          //  refresh window 
 
5830
 
 
5831
   return;
 
5832
}
 
5833
 
 
5834
 
 
5835
//  private function
 
5836
//  mouse event function for index window - get selected thumbnail and file
 
5837
 
 
5838
void image_navi::mouse_xevent(GtkWidget *, GdkEventButton *event, void *)
 
5839
{
 
5840
   int            mousex, mousey;
 
5841
   int            row, col, Nth, err;
 
5842
   char           *filez;
 
5843
   struct stat    statb;
 
5844
   
 
5845
   mousex = int(event->x);
 
5846
   mousey = int(event->y);
 
5847
 
 
5848
   row = (mousey - xmargH) / thumbH;                                       //  find selected row, col
 
5849
   col = (mousex - xmargW) / thumbW;
 
5850
 
 
5851
   if (row < 0 || row >= xrows) return;
 
5852
   if (col < 0 || col >= xcols) return;
 
5853
   
 
5854
   Nth = xcols * row + col;
 
5855
 
 
5856
   if (Nth == 0) filez = strdupz(filex);                                   //  anchor file selected
 
5857
   else  {
 
5858
      filez = image_navigate(filex,"next",Nth);                            //  else get Nth file after anchor
 
5859
      if (! filez) return;
 
5860
   }
 
5861
   
 
5862
   err = stat(filez,&statb);                                               //  file is gone?
 
5863
   if (err) {
 
5864
      image_navigate(filex,"init");                                        //  initz. window    v.2.11
 
5865
      windx_paint();
 
5866
      return;
 
5867
   }
 
5868
 
 
5869
   if (S_ISDIR(statb.st_mode)) {                                           //  if directory, go there
 
5870
      image_navigate(filez,"init");
 
5871
      if (filex) zfree(filex);
 
5872
      filex = image_navigate(filez,"first");                               //  set anchor file 
 
5873
      windx_paint();
 
5874
      return;
 
5875
   }
 
5876
   
 
5877
   if (userfunc) userfunc(filez);                                          //  pass clicked file to user function
 
5878
   return;
 
5879
}
 
5880
 
 
5881
 
 
5882
//  private function
 
5883
//  KB event function - respond to keyboard navigation keys
 
5884
 
 
5885
int image_navi::KBxpress(GtkWidget *win, GdkEventKey *event, void *)       //  prevent propagation of key-press
 
5886
{                                                                          //    events to toolbar buttons  v.2.9
 
5887
   return 1;
 
5888
}
 
5889
 
 
5890
int image_navi::KBxrelease(GtkWidget *win, GdkEventKey *event, void *)
 
5891
{
 
5892
   int      KBkey;
 
5893
   
 
5894
   KBkey = event->keyval;
 
5895
 
 
5896
   if (KBkey == GDK_plus) menufuncx(win,ZTX("bigger"));                    //  +/- = bigger/smaller thumbnails
 
5897
   if (KBkey == GDK_equal) menufuncx(win,ZTX("bigger"));                   //                            v.2.5
 
5898
   if (KBkey == GDK_minus) menufuncx(win,ZTX("smaller"));
 
5899
 
 
5900
   if (KBkey == GDK_Left) menufuncx(win,ZTX("prev page"));                 //  left arrow = previous page
 
5901
   if (KBkey == GDK_Right) menufuncx(win,ZTX("next page"));                //  right arrow = next page
 
5902
   if (KBkey == GDK_Up) menufuncx(win,ZTX("prev row"));                    //  up arrow = previous row
 
5903
   if (KBkey == GDK_Down) menufuncx(win,ZTX("next row"));                  //  down arrow = next row
 
5904
 
 
5905
   if (KBkey == GDK_Escape) gtk_widget_destroy(win);                       //  Escape = quit thumbnail window
 
5906
   
 
5907
   return 1;
 
5908
}
 
5909
 
 
5910
 
 
5911
/**************************************************************************
 
5912
 
 
5913
   private function - manage list of image files within a directory
 
5914
 
 
5915
   get an image file in the same directory as given file or directory
 
5916
 
 
5917
   action: init      initz. list of image files and subdirectories
 
5918
           initF     initz. from given file list
 
5919
           find      return filez if in list, else null
 
5920
           prev      return filez Nth previous file
 
5921
           next      return filez Nth next file
 
5922
           first     return first file in list
 
5923
           last      return Nth last file in list
 
5924
 
 
5925
   Nth = 1  means filez -1, filez +1, and last file
 
5926
 
 
5927
   For init and initF, null is returned.
 
5928
   For the others, filespec is returned or null if not found.
 
5929
   Returned file belongs to caller and is a subject for zfree().
 
5930
 
 
5931
***************************************************************************/
 
5932
 
 
5933
char * image_navi::image_navigate(cchar *filez, cchar *action, int Nth)
 
5934
{
 
5935
   char           *buff;
 
5936
   const char     *findcommand = "find \"%s\" -maxdepth 1";
 
5937
   char           filezz[maxfcc], *pp, *file2;
 
5938
   int            err, ii, nn = 0, ftyp, contx = 0;
 
5939
   FILE           *fid;
 
5940
   struct stat    statbuf;
 
5941
   
 
5942
   if (! strstr("init initF find first last prev next",action))            //  v.2.5
 
5943
         zappcrash("image_navigate %s",action);
 
5944
 
 
5945
   if (strEqu(action,"init"))                                              //  initialize from given directory
 
5946
   {
 
5947
      for (int ii = 0; ii < nfiles; ii++) zfree(flist[ii]);                //  free prior list memory
 
5948
      if (flist) zfree(flist);
 
5949
      flist = (char **) zmalloc(flimit * sizeof(char *));                  //  list of file pointers
 
5950
 
 
5951
      nfiles = 0;                                                          //  no files
 
5952
      if (filex) zfree(filex);                                             //  no anchor file
 
5953
      filex = 0;
 
5954
 
 
5955
      strncpy0(dirkx,filez,maxfcc-2);                                      //  copy input file
 
5956
      err = stat(dirkx,&statbuf);
 
5957
      if (err) {
 
5958
         pp = (char *) strrchr(dirkx,'/');                                 //  bad file, check directory part  
 
5959
         if (! pp) return 0;
 
5960
         pp[1] = 0;
 
5961
         err = stat(dirkx,&statbuf);
 
5962
         if (err) return 0;                                                //  give up, empty file list
 
5963
      }
 
5964
 
 
5965
      if (S_ISREG(statbuf.st_mode)) {                                      //  if a file, get directory part
 
5966
         pp = (char *) strrchr(dirkx,'/');
 
5967
         if (! pp) return 0;
 
5968
         pp[1] = 0;
 
5969
      }
 
5970
 
 
5971
      while ((buff = command_output(contx,findcommand,dirkx)))             //  find all files
 
5972
      {
 
5973
         if (strEqu(buff,dirkx)) {                                         //  skip self directory
 
5974
            zfree(buff);
 
5975
            continue;
 
5976
         }
 
5977
         
 
5978
         ftyp = image_file_type(buff);
 
5979
         if (ftyp != 1 && ftyp != 2) {                                     //  not directory or image file type
 
5980
            zfree(buff);                                                   //  (.thumbnails not ftyp 1)
 
5981
            continue;
 
5982
         }
 
5983
 
 
5984
         flist[nfiles] = buff;                                             //  add to file list
 
5985
         if (ftyp == 1) flist[nfiles][0] = '!';                            //  if directory, make it sort first
 
5986
         nfiles++;
 
5987
         if (nfiles == flimit) zappcrash("more than %d files",flimit);
 
5988
      }
 
5989
 
 
5990
      if (nfiles == 0) return 0;                                           //  no files
 
5991
 
 
5992
      HeapSort(flist,nfiles,image_fcomp);                                  //  Heap Sort - pointers to strings
 
5993
   }
 
5994
 
 
5995
   if (strEqu(action,"initF"))                                             //  initialize from given list  v.2.4
 
5996
   {
 
5997
      for (int ii = 0; ii < nfiles; ii++) zfree(flist[ii]);                //  free prior list memory
 
5998
      if (flist) zfree(flist);
 
5999
      flist = (char **) zmalloc(flimit * sizeof(char *));                  //  list of file pointers
 
6000
 
 
6001
      nfiles = 0;                                                          //  no files
 
6002
      if (filex) zfree(filex);                                             //  no anchor file
 
6003
      filex = 0;
 
6004
 
 
6005
      strncpy0(dirkx,filez,maxfcc-1);                                      //  copy input file
 
6006
      
 
6007
      fid = fopen(dirkx,"r");                                              //  open file
 
6008
      if (! fid) return 0;
 
6009
 
 
6010
      buff = zmalloc(maxfcc);
 
6011
      
 
6012
      while (true)                                                         //  read list of files
 
6013
      {
 
6014
         pp = fgets_trim(buff,maxfcc-1,fid,1);
 
6015
         if (! pp) break;
 
6016
         flist[nfiles] = strdupz(buff);                                    //  add files to memory list
 
6017
         nfiles++;
 
6018
         if (nfiles == flimit) zappcrash("more than %d files",flimit);
 
6019
      }
 
6020
      
 
6021
      fclose(fid);
 
6022
      zfree(buff);
 
6023
 
 
6024
      if (nfiles == 0) return 0;                                           //  no files
 
6025
 
 
6026
      HeapSort(flist,nfiles,image_fcomp);                                  //  Heap Sort - pointers to strings
 
6027
   }
 
6028
 
 
6029
   if (nfiles == 0) return 0;                                              //  file list is empty
 
6030
   
 
6031
   if (! filez) *filezz = 0;                                               //  no filez arg               v.2.5
 
6032
   else strncpy0(filezz,filez,maxfcc);                                     //  make modifiable copy of filez
 
6033
   err = stat(filezz,&statbuf);
 
6034
   if (! err && S_ISDIR(statbuf.st_mode)) filezz[0] = '!';                 //  if directory, match with sorted list
 
6035
 
 
6036
   for (ii = 0; ii < nfiles; ii++) {                                       //  search for filez in file list
 
6037
      nn = image_fcomp(filezz,flist[ii]);                                  //  = 0: filez = flist[ii]
 
6038
      if (nn <= 0) break;                                                  //  < 0: flist[ii-1] < filez < flist[ii]
 
6039
   }
 
6040
   
 
6041
   if (ii == nfiles) ii--;                                                 //  filez > last file in list
 
6042
 
 
6043
   if (strnEqu(action,"init",4)) {                                         //  init or initF
 
6044
      filex = strdupz(flist[ii]);                                          //  anchor file = 1st file
 
6045
      filex[0] = '/';                                                      //  restore initial '/'
 
6046
      return 0;
 
6047
   }
 
6048
   
 
6049
   if (strEqu(action,"find"))                                              //  is filez in list or not   v.2.4
 
6050
      if (nn != 0) return 0;
 
6051
 
 
6052
   if (strEqu(action,"first"))                                             //  return first file
 
6053
      ii = 0;
 
6054
 
 
6055
   if (strEqu(action,"last")) {                                            //  Nth last file (1 = last)
 
6056
      ii = nfiles - Nth;
 
6057
      if (ii < 0) ii = 0;                                                  //  if < first file, return first
 
6058
   }
 
6059
   
 
6060
   if (strEqu(action,"prev")) {                                            //  Nth previous file
 
6061
      if (ii == 0) return 0;                                               //  filez <= first in list
 
6062
      ii -= Nth;
 
6063
      if (ii < 0) ii = 0;                                                  //  first file
 
6064
   }
 
6065
   
 
6066
   if (strEqu(action,"next")) {                                            //  Nth next file
 
6067
      if (nn == 0) ii += Nth;                                              //  get Nth next file       v.2.6
 
6068
      if (ii > nfiles-1) return 0;                                         //  beyond last file
 
6069
   }
 
6070
   
 
6071
   file2 = strdupz(flist[ii]);                                             //  copy file into new memory
 
6072
   file2[0] = '/';                                                         //  restore initial '/'
 
6073
   err = stat(file2,&statbuf);
 
6074
   if (! err) return file2;                                                //  return file
 
6075
 
 
6076
   if (strcmpv(action,"prev","next",null))                                 //  try to skip over missing file
 
6077
      return image_navigate(file2,action,1);                               //                     v.2.16
 
6078
   else return 0;
 
6079
}
 
6080
 
 
6081
 
 
6082
//  private function for special file name compare
 
6083
//  directories sort first, upper/lower case names sort together
 
6084
 
 
6085
int image_navi::image_fcomp(const char *file1, const char *file2)
 
6086
{
 
6087
   int      nn;
 
6088
   nn = strcasecmp(file1,file2);                                           //  compare ignoring case
 
6089
   if (nn != 0) return nn;
 
6090
   nn = strcmp(file1,file2);                                               //  if equal, do normal compare
 
6091
   return nn;
 
6092
}
 
6093
 
 
6094
using namespace std;
 
6095
 
 
6096
 
 
6097
/**************************************************************************
 
6098
    parameter management functions
 
6099
***************************************************************************/
 
6100
 
 
6101
struct t_parmlist {                                                        //  parameter list in memory
 
6102
   int      max;                                                           //  max parameter count
 
6103
   int      count;                                                         //  actual parameter count
 
6104
   char     **name;                                                        //  pointer to names (list of char *)
 
6105
   double   *value;                                                        //  pointer to values (list of double)
 
6106
} parmlist;
 
6107
 
 
6108
int      parmlistvalid = 0;                                                //  flag
 
6109
char     zparmfile[1000];                                                  //  last used parm file
 
6110
 
 
6111
 
 
6112
//  initialize parameter list - must be called first
 
6113
 
 
6114
int initParmlist(int max)
 
6115
{
 
6116
   if (! parmlistvalid) {                                                  //  start with default parms file
 
6117
      strcpy(zparmfile,get_zuserdir());
 
6118
      strcat(zparmfile,"/parameters");                                     //  /home/user/.appname/parameters
 
6119
   }
 
6120
   
 
6121
   if (parmlistvalid) {                                                    //  delete old parms
 
6122
      delete [] parmlist.name;
 
6123
      delete [] parmlist.value;
 
6124
   }
 
6125
 
 
6126
   parmlist.max = max;
 
6127
   parmlist.count = 0;
 
6128
   char **names = new char*[max];                                          //  allocate max pointers for names
 
6129
   double *values = new double[max];                                       //  allocate max doubles for values
 
6130
   parmlist.name = names;
 
6131
   parmlist.value = values;
 
6132
   parmlistvalid = 1;
 
6133
   return 0;
 
6134
}
 
6135
 
 
6136
 
 
6137
//  Load user parameters if the file exists, else initialize the
 
6138
//  user parameters file from default application parameters.
 
6139
 
 
6140
int initz_userParms()
 
6141
{
 
6142
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6143
 
 
6144
   int np = loadParms("parameters");
 
6145
   if (! np) {
 
6146
      saveParms("parameters");
 
6147
      zmessageACK(ZTX("Initial parameters file created. \n"
 
6148
                      "Inspect and revise if necessary."));
 
6149
   }
 
6150
   return np;
 
6151
}
 
6152
 
 
6153
 
 
6154
//  load parameters from a file, with file selection dialog
 
6155
 
 
6156
int loadParms()
 
6157
{
 
6158
   char     *pfile;
 
6159
   int      np;
 
6160
   
 
6161
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6162
 
 
6163
   pfile = zgetfile(ZTX("load parameters from a file"),zparmfile,"open","hidden");
 
6164
   if (! pfile) return 0;
 
6165
   
 
6166
   np = loadParms(pfile);
 
6167
   zfree(pfile);
 
6168
 
 
6169
   return np;
 
6170
}
 
6171
   
 
6172
 
 
6173
//  load parameters from a file
 
6174
//  returns no. parameters loaded
 
6175
 
 
6176
int loadParms(const char *pfile)
 
6177
{
 
6178
   FILE        *fid;
 
6179
   int         Nth, np1, np2 = 0, err;
 
6180
   char        buff[100], *fgs, *pp;
 
6181
   cchar       *pname, *pvalue;
 
6182
   double      dvalue;
 
6183
   
 
6184
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6185
 
 
6186
   if (! pfile) pfile = zparmfile;
 
6187
   
 
6188
   if (*pfile != '/') {                                                    //  if parm file name only,
 
6189
      pp = (char *) strrchr(zparmfile,'/');                                //    make complete absolute path
 
6190
      if (pp) strcpy(pp+1,pfile);                                          //      in same directory as prior
 
6191
      pfile = zparmfile;
 
6192
   }
 
6193
 
 
6194
   fid = fopen(pfile,"r");
 
6195
   if (! fid) return 0;                                                    //  bad file
 
6196
   strncpy0(zparmfile,pfile,999);                                          //  set current parm file
 
6197
 
 
6198
   while (true)                                                            //  read file
 
6199
   {
 
6200
      fgs = fgets_trim(buff,99,fid,1);
 
6201
      if (! fgs) break;                                                    //  EOF
 
6202
 
 
6203
      pp = strchr(buff,'#');                                               //  eliminate comments
 
6204
      if (pp) *pp = 0;
 
6205
 
 
6206
      Nth = 1;                                                             //  parse parm name, value
 
6207
      pname = strField(buff,' ',Nth++);
 
6208
      if (! pname) continue;
 
6209
      pvalue = strField(buff,' ',Nth);
 
6210
      if (! pvalue) continue;
 
6211
      err = convSD(pvalue,dvalue);
 
6212
      if (err) continue;
 
6213
      np1 = setParm(pname,dvalue);                                         //  set the parameter
 
6214
      if (! np1) continue;
 
6215
      np2++;
 
6216
   }
 
6217
   
 
6218
   fclose(fid);                                                            //  close file
 
6219
   return np2;                                                             //  return parameter count
 
6220
}
 
6221
 
 
6222
 
 
6223
//  save parameters to a file, with file selection dialog
 
6224
 
 
6225
int saveParms()
 
6226
{
 
6227
   char     *pfile;
 
6228
   int      np;
 
6229
 
 
6230
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6231
 
 
6232
   pfile = zgetfile(ZTX("save parameters to a file"),zparmfile,"save","hidden");
 
6233
   if (! pfile) return 0;
 
6234
 
 
6235
   np = saveParms(pfile);
 
6236
   zfree(pfile);
 
6237
 
 
6238
   return np;
 
6239
}
 
6240
 
 
6241
 
 
6242
//  save parameters to a file
 
6243
 
 
6244
int saveParms(const char *pfile)
 
6245
{
 
6246
   FILE     *fid;
 
6247
   int      np;
 
6248
   char     *pp;
 
6249
 
 
6250
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6251
 
 
6252
   if (*pfile != '/') {                                                    //  if parm file name only,
 
6253
      pp = (char *) strrchr(zparmfile,'/');                                //    make complete absolute path
 
6254
      if (pp) strcpy(pp+1,pfile);                                          //      in same directory as prior
 
6255
      pfile = zparmfile;
 
6256
   }
 
6257
 
 
6258
   fid = fopen(pfile,"w");
 
6259
   if (! fid) { 
 
6260
      zmessageACK(ZTX("cannot open file %s"),pfile);
 
6261
      return 0;
 
6262
   }
 
6263
 
 
6264
   strncpy0(zparmfile,pfile,999);
 
6265
 
 
6266
   for (np = 0; np < parmlist.count; np++)
 
6267
      fprintf(fid," \"%s\"  %.12g \n",parmlist.name[np],parmlist.value[np]);
 
6268
   
 
6269
   fclose(fid);
 
6270
   return np;
 
6271
}
 
6272
 
 
6273
 
 
6274
//  create a new paramater or change value of existing parameter
 
6275
 
 
6276
int setParm(const char *parmname, double parmval)
 
6277
{
 
6278
   int      ii, cc;
 
6279
   char     *ppname;
 
6280
 
 
6281
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6282
 
 
6283
   for (ii = 0; ii < parmlist.count; ii++)
 
6284
      if (strEqu(parmlist.name[ii],parmname)) break;
 
6285
 
 
6286
   if (ii == parmlist.max) return 0;
 
6287
 
 
6288
   if (ii == parmlist.count) {
 
6289
      parmlist.count++;
 
6290
      cc = strlen(parmname);
 
6291
      ppname = new char[cc+1];
 
6292
      strTrim(ppname,parmname);
 
6293
      parmlist.name[ii] = ppname;
 
6294
   }
 
6295
 
 
6296
   parmlist.value[ii] = parmval;
 
6297
   return parmlist.count;
 
6298
}
 
6299
 
 
6300
 
 
6301
//  get parameter value from parameter name
 
6302
 
 
6303
double getParm(const char *parmname)
 
6304
{
 
6305
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6306
 
 
6307
   for (int ii = 0; ii < parmlist.count; ii++)
 
6308
   {
 
6309
      if (strNeq(parmlist.name[ii],parmname)) continue;
 
6310
      return parmlist.value[ii];
 
6311
   }
 
6312
 
 
6313
   return NAN;
 
6314
}
 
6315
 
 
6316
 
 
6317
//  get Nth parameter name (zero-based)
 
6318
 
 
6319
char * getParm(int Nth)
 
6320
{
 
6321
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6322
   if (Nth >= parmlist.count) return null;
 
6323
   return parmlist.name[Nth];
 
6324
}
 
6325
 
 
6326
 
 
6327
//  list parameters in supplied text entry window
 
6328
 
 
6329
int listParms(GtkWidget *textWin)
 
6330
{
 
6331
   int            ii;
 
6332
   const char     *pname;
 
6333
   double         pvalue;
 
6334
 
 
6335
   for (ii = 0; ii < parmlist.count; ii++)
 
6336
   {
 
6337
      pname = getParm(ii);
 
6338
      pvalue = getParm(pname);
 
6339
      wprintf(textWin," %s  %.12g \n",pname,pvalue);
 
6340
   }
 
6341
   
 
6342
   return parmlist.count;
 
6343
}
 
6344
 
 
6345
 
 
6346
//  edit parameters with a GUI
 
6347
//  textWin != null enables button to list parameters in window
 
6348
//  addp != 0 enables button to add new parameters
 
6349
//  return: 0 if cancel, else parameter count                              //  v.2.7
 
6350
 
 
6351
int editParms(GtkWidget *textWin, int addp)
 
6352
{
 
6353
   GtkWidget      *peDialog, *peLabel[100], *peEdit[100], *peHbox[100];
 
6354
   char           ptemp[20];
 
6355
   const char     *pchval, *pname;
 
6356
   double         pvalue;
 
6357
   int            ii, err, iie = -1, zstat, floaded = 0;
 
6358
   int            bcancel=1, bapply=2, bload=3, bsave=4, blist=5, baddp=6;
 
6359
   
 
6360
   if (! parmlistvalid) zappcrash("parmlistvalid = 0");
 
6361
 
 
6362
   build_dialog:                                                           //  build parameter edit dialog
 
6363
 
 
6364
   if (parmlist.count > 100) zappcrash("more than 100 parameters");
 
6365
   
 
6366
   if (textWin && addp) 
 
6367
       peDialog = gtk_dialog_new_with_buttons
 
6368
         (ZTX("edit parameters"), null, GTK_DIALOG_MODAL, 
 
6369
          ZTX("load\nfile"),bload, ZTX("save\nfile"),bsave, ZTX("list\nall"),blist, 
 
6370
          ZTX("add\nnew"),baddp, ZTX("cancel"),bcancel, ZTX("apply"),bapply, null);
 
6371
 
 
6372
   else if (textWin) 
 
6373
      peDialog = gtk_dialog_new_with_buttons
 
6374
         (ZTX("edit parameters"), null, GTK_DIALOG_MODAL, 
 
6375
          ZTX("load\nfile"),bload, ZTX("save\nfile"),bsave, ZTX("list\nall"),blist, 
 
6376
          ZTX("cancel"),bcancel, ZTX("apply"),bapply, null);
 
6377
 
 
6378
   else if (addp) 
 
6379
      peDialog = gtk_dialog_new_with_buttons
 
6380
         (ZTX("edit parameters"), null, GTK_DIALOG_MODAL, 
 
6381
          ZTX("load\nfile"),bload, ZTX("save\nfile"),bsave,  
 
6382
          ZTX("add\nnew"),baddp, ZTX("cancel"),bcancel, ZTX("apply"),bapply, null);
 
6383
 
 
6384
   else peDialog = 
 
6385
      gtk_dialog_new_with_buttons
 
6386
         (ZTX("edit parameters"), null, GTK_DIALOG_MODAL, 
 
6387
          ZTX("load\nfile"),bload, ZTX("save\nfile"),bsave,  
 
6388
          ZTX("cancel"),bcancel, ZTX("apply"),bapply, null);
 
6389
 
 
6390
   gtk_window_set_position(GTK_WINDOW(peDialog),GTK_WIN_POS_MOUSE);        //  v.2.7.1
 
6391
 
 
6392
   for (ii = 0; ii < parmlist.count; ii++)                                 //  labels and edit boxes side by side
 
6393
   {                                                                       //  (parm names and parm values)
 
6394
      peLabel[ii] = gtk_label_new(parmlist.name[ii]);
 
6395
      gtk_misc_set_alignment(GTK_MISC(peLabel[ii]),1,0.5);
 
6396
      gtk_label_set_width_chars(GTK_LABEL(peLabel[ii]),30);
 
6397
      peEdit[ii] = gtk_entry_new();
 
6398
      gtk_entry_set_width_chars(GTK_ENTRY(peEdit[ii]),12);
 
6399
      sprintf(ptemp,"%.12g",parmlist.value[ii]);
 
6400
      gtk_entry_set_text(GTK_ENTRY(peEdit[ii]),ptemp);
 
6401
      peHbox[ii] = gtk_hbox_new(0,0);
 
6402
      gtk_box_pack_start(GTK_BOX(peHbox[ii]),peLabel[ii],0,0,5);
 
6403
      gtk_box_pack_start(GTK_BOX(peHbox[ii]),peEdit[ii],0,0,5);
 
6404
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(peDialog)->vbox),peHbox[ii],1,1,2);
 
6405
   }
 
6406
 
 
6407
   run_dialog:                                                             //  display dialog and get inputs
 
6408
   
 
6409
   if (iie > -1)
 
6410
   {
 
6411
      gtk_editable_select_region(GTK_EDITABLE(peEdit[iie]),0,-1);          //  focus on new or bad parameter
 
6412
      gtk_widget_grab_focus(peEdit[iie]);
 
6413
      iie = -1;
 
6414
   }
 
6415
 
 
6416
   gtk_widget_show_all(peDialog);
 
6417
   zstat = gtk_dialog_run(GTK_DIALOG(peDialog));
 
6418
 
 
6419
   if (zstat <= bcancel)                                                   //  kill, cancel
 
6420
   {
 
6421
      if (floaded) {
 
6422
         zstat = zmessageYN(ZTX("apply?"));                                //  if file loaded, clarify  v.2.9
 
6423
         if (! zstat) { 
 
6424
            gtk_widget_destroy(peDialog);
 
6425
            return 0;
 
6426
         }
 
6427
         zstat = bapply;
 
6428
      }
 
6429
   }
 
6430
   
 
6431
   if (zstat == bload)                                                     //  load from file
 
6432
   {
 
6433
      loadParms();
 
6434
      gtk_widget_destroy(peDialog);
 
6435
      floaded = 1;
 
6436
      goto build_dialog;
 
6437
   }
 
6438
   
 
6439
   for (ii = 0; ii < parmlist.count; ii++)                                 //  capture inputs and check if OK
 
6440
   {
 
6441
      pchval = gtk_entry_get_text(GTK_ENTRY(peEdit[ii]));
 
6442
      err = convSD(pchval,pvalue);
 
6443
      if (err && iie < 0) iie = ii;                                        //  remember 1st error
 
6444
   }
 
6445
 
 
6446
   if (iie >= 0) goto run_dialog;                                          //  re-get bad input
 
6447
 
 
6448
   if (zstat == bapply)                                                    //  apply new values
 
6449
   {
 
6450
      for (ii = 0; ii < parmlist.count; ii++)                              //  capture inputs and save them  v.2.7
 
6451
      {
 
6452
         pchval = gtk_entry_get_text(GTK_ENTRY(peEdit[ii]));
 
6453
         err = convSD(pchval,parmlist.value[ii]);
 
6454
      }
 
6455
      gtk_widget_destroy(peDialog);                                        //  done
 
6456
      return parmlist.count;
 
6457
   }
 
6458
 
 
6459
   if (zstat == bsave)                                                     //  save to file
 
6460
   {
 
6461
      for (ii = 0; ii < parmlist.count; ii++)                              //  apply new values  v.2.9
 
6462
      {
 
6463
         pchval = gtk_entry_get_text(GTK_ENTRY(peEdit[ii]));
 
6464
         err = convSD(pchval,parmlist.value[ii]);
 
6465
      }
 
6466
      saveParms();
 
6467
      floaded = 0;
 
6468
      goto run_dialog;
 
6469
   }
 
6470
   
 
6471
   if (zstat == blist)                                                     //  list parameters
 
6472
   {
 
6473
      listParms(textWin);
 
6474
      goto run_dialog;
 
6475
   }
 
6476
   
 
6477
   if (zstat == baddp)                                                     //  add parameter
 
6478
   {
 
6479
      pname = dialogText(ZTX("add parameter"),ZTX("(new parm name)"));
 
6480
      if (! pname) goto run_dialog;
 
6481
      setParm(pname,0.0);
 
6482
      floaded = 1;
 
6483
      iie = parmlist.count - 1;                                            //  focus on new parm
 
6484
      gtk_widget_destroy(peDialog);
 
6485
      goto build_dialog;
 
6486
   }
 
6487
   
 
6488
   gtk_widget_destroy(peDialog);                                           //  unknown status
 
6489
   return 0;
 
6490
}
 
6491
 
 
6492
 
 
6493
/**************************************************************************
 
6494
      xstring class (dynamic length string)
 
6495
***************************************************************************/
 
6496
 
 
6497
#define  wmiv  1648734981
 
6498
 
 
6499
int   xstring::tcount = 0;                                                 //  initz. static members
 
6500
int   xstring::tmem = 0;
 
6501
 
 
6502
 
 
6503
xstring::xstring(int cc)                                                   //  new xstring(cc)
 
6504
{
 
6505
   wmi = wmiv;
 
6506
   xmem = (cc & 0x7ffffff8) + 8;                                           //  mod 8 length
 
6507
   xpp = new char[xmem];                                                   //  allocate
 
6508
   if (! xpp) appcrash("xstring NEW failure",null);
 
6509
   tcount++;                                                               //  incr. object count
 
6510
   tmem += xmem;                                                           //  incr. allocated memory
 
6511
   xcc = 0;                                                                //  string cc = 0
 
6512
   *xpp = 0;                                                               //  string = null
 
6513
}
 
6514
 
 
6515
 
 
6516
xstring::xstring(const char *string)                                       //  new xstring("initial string")
 
6517
{
 
6518
   wmi = wmiv;
 
6519
   xcc = 0;
 
6520
   if (string) xcc = strlen(string);                                       //  string length
 
6521
   xmem = (xcc & 0x7ffffff8) + 8;                                          //  mod 8 length
 
6522
   xpp = new char[xmem];                                                   //  allocate
 
6523
   if (! xpp) appcrash("xstring NEW failure",null);
 
6524
   tcount++;                                                               //  incr. object count
 
6525
   tmem += xmem;                                                           //  incr. allocated memory
 
6526
   *xpp = 0;
 
6527
   if (xcc) strcpy(xpp,string);                                            //  copy string
 
6528
}
 
6529
 
 
6530
 
 
6531
xstring::xstring(const xstring & xstr)                                     //  new xstring2(xstring1)
 
6532
{
 
6533
   wmi = wmiv;
 
6534
   xmem = xstr.xmem;                                                       //  allocate same length
 
6535
   xcc = xstr.xcc;
 
6536
   xpp = new char[xmem];
 
6537
   if (! xpp) appcrash("xstring NEW failure",null);
 
6538
   tcount++;                                                               //  incr. object count
 
6539
   tmem += xmem;                                                           //  incr. allocated memory
 
6540
   strcpy(xpp,xstr.xpp);                                                   //  copy string
 
6541
}
 
6542
 
 
6543
 
 
6544
xstring::~xstring()                                                        //  delete xstring
 
6545
{  
 
6546
   validate();
 
6547
   delete[] xpp;                                                           //  release allocated memory
 
6548
   xpp = 0;
 
6549
   tcount--;                                                               //  decr. object count
 
6550
   tmem -= xmem;                                                           //  decr. allocated memory
 
6551
   if (tcount < 0) appcrash("xstring count < 0",null);
 
6552
   if (tmem < 0) appcrash("xstring memory < 0",null);
 
6553
   if (tcount == 0 && tmem > 0) appcrash("xstring memory leak",null);
 
6554
}
 
6555
 
 
6556
 
 
6557
xstring xstring::operator= (const xstring & xstr)                          //  xstring2 = xstring1
 
6558
{
 
6559
   validate();
 
6560
   xstr.validate();
 
6561
   if (this == &xstr) return *this;
 
6562
   xcc = xstr.xcc;
 
6563
   if (xmem < xcc+1)
 
6564
   {
 
6565
      delete[] xpp;                                                        //  expand memory if needed
 
6566
      tmem -= xmem;
 
6567
      xmem = (xcc & 0x7ffffff8) + 8;                                       //  mod 8 length
 
6568
      xpp = new char[xmem];
 
6569
      if (! xpp) appcrash("xstring NEW failure",null);
 
6570
      tmem += xmem;
 
6571
   }
 
6572
   strcpy(xpp,xstr.xpp);                                                   //  copy string
 
6573
   return *this;
 
6574
}
 
6575
 
 
6576
 
 
6577
xstring xstring::operator= (const char *str)                               //  xstring = "some string"
 
6578
{
 
6579
   validate();
 
6580
   xcc = 0;
 
6581
   *xpp = 0;
 
6582
   if (str) xcc = strlen(str);
 
6583
   if (xmem < xcc+1)
 
6584
   {
 
6585
      delete[] xpp;                                                        //  expand memory if needed
 
6586
      tmem -= xmem;
 
6587
      xmem = (xcc & 0x7ffffff8) + 8;                                       //  mod 8 length
 
6588
      xpp = new char[xmem];
 
6589
      if (! xpp) appcrash("xstring NEW failure",null);
 
6590
      tmem += xmem;
 
6591
   }
 
6592
   if (xcc) strcpy(xpp,str);                                               //  copy string
 
6593
   return *this;
 
6594
}
 
6595
 
 
6596
 
 
6597
xstring operator+ (const xstring & x1, const xstring & x2)                 //  xstring1 + xstring2
 
6598
{
 
6599
   x1.validate();
 
6600
   x2.validate();
 
6601
   xstring temp(x1.xcc + x2.xcc);                                          //  build temp xstring
 
6602
   strcpy(temp.xpp,x1.xpp);                                                //    with both input strings
 
6603
   strcpy(temp.xpp + x1.xcc, x2.xpp);
 
6604
   temp.xcc = x1.xcc + x2.xcc;
 
6605
   temp.validate();
 
6606
   return temp;
 
6607
}
 
6608
 
 
6609
 
 
6610
xstring operator+ (const xstring & x1, const char *s2)                     //  xstring + "some string"
 
6611
{
 
6612
   x1.validate();
 
6613
   int cc2 = 0;
 
6614
   if (s2) cc2 = strlen(s2);
 
6615
   xstring temp(x1.xcc + cc2);                                             //  build temp xstring
 
6616
   strcpy(temp.xpp,x1.xpp);                                                //    with both input strings
 
6617
   if (s2) strcpy(temp.xpp + x1.xcc, s2);
 
6618
   temp.xcc = x1.xcc + cc2;
 
6619
   temp.validate();
 
6620
   return temp;
 
6621
}
 
6622
 
 
6623
 
 
6624
xstring operator+ (const char *s1, const xstring & x2)                     //  "some string" + xstring
 
6625
{
 
6626
   x2.validate();
 
6627
   int cc1 = 0;
 
6628
   if (s1) cc1 = strlen(s1);
 
6629
   xstring temp(cc1 + x2.xcc);                                             //  build temp xstring
 
6630
   if (s1) strcpy(temp.xpp,s1);                                            //    with both input strings
 
6631
   strcpy(temp.xpp + cc1, x2.xpp);
 
6632
   temp.xcc = cc1 + x2.xcc;
 
6633
   temp.validate();
 
6634
   return temp;
 
6635
}
 
6636
 
 
6637
 
 
6638
void xstring::insert(int pos, const char *string, int cc)                  //  insert cc chars from string at pos
 
6639
{                                                                          //  pad if pos > xcc or cc > string
 
6640
   validate();
 
6641
 
 
6642
   int scc = strlen(string);
 
6643
   if (! cc) cc = scc;
 
6644
 
 
6645
   int pad = pos - xcc;
 
6646
   if (pad < 0) pad = 0;                                
 
6647
 
 
6648
   if (xmem < xcc + cc + pad + 1)                                          //  allocate more memory if needed
 
6649
   {
 
6650
      int newmem = xcc + cc + pad;
 
6651
      newmem = (newmem & 0x7ffffff8) + 8;                                  //  mod 8 length
 
6652
      char * xpp2 = new char[newmem];
 
6653
      if (! xpp2) appcrash("xstring NEW failure",null);
 
6654
      strcpy(xpp2,xpp);                                                    //  copy to new space
 
6655
      delete[] xpp;
 
6656
      xpp = xpp2;
 
6657
      tmem += newmem - xmem;
 
6658
      xmem = newmem;
 
6659
   }
 
6660
 
 
6661
   if (pad) memset(xpp+xcc,' ',pad);                                       //  add blanks up to pos
 
6662
 
 
6663
   for (int ii = xcc + pad; ii >= pos; ii--)                               //  make hole for inserted string
 
6664
           *(xpp+ii+cc) = *(xpp+ii);
 
6665
 
 
6666
   if (cc > scc) memset(xpp+pos+scc,' ',cc-scc);                           //  blank pad if cc > string
 
6667
   if (cc < scc) scc = cc;
 
6668
   strncpy(xpp+pos,string,scc);                                            //  insert string, without null
 
6669
 
 
6670
   xcc += cc + pad;                                                        //  set new length
 
6671
   xpp[xcc] = 0;
 
6672
   validate();
 
6673
}
 
6674
 
 
6675
 
 
6676
void xstring::overlay(int pos, const char *string, int cc)                 //  overlay substring
 
6677
{
 
6678
   validate();
 
6679
 
 
6680
   int scc = strlen(string);
 
6681
   if (! cc) cc = scc;
 
6682
 
 
6683
   if (xmem < pos + cc + 1)                                                //  allocate more memory if needed
 
6684
   {
 
6685
      int newmem = pos + cc;
 
6686
      newmem = (newmem & 0x7ffffff8) + 8;                                  //  mod 8 length
 
6687
      char * xpp2 = new char[newmem];
 
6688
      if (! xpp2) appcrash("xstring NEW failure",null);
 
6689
      strcpy(xpp2,xpp);                                                    //  copy to new space
 
6690
      delete[] xpp;
 
6691
      xpp = xpp2;
 
6692
      tmem += newmem - xmem;
 
6693
      xmem = newmem;
 
6694
   }
 
6695
 
 
6696
   if (pos > xcc) memset(xpp+xcc,' ',pos-xcc);                             //  add blanks up to pos
 
6697
   
 
6698
   if (cc > scc) memset(xpp+pos+scc,' ',cc-scc);                           //  blank pad if cc > string
 
6699
   if (cc < scc) scc = cc;
 
6700
   strncpy(xpp+pos,string,scc);                                            //  insert string, without null
 
6701
 
 
6702
   if (pos + cc > xcc) xcc = pos + cc;                                     //  set new length 
 
6703
   xpp[xcc] = 0;
 
6704
   validate();
 
6705
}
 
6706
 
 
6707
 
 
6708
void xstring::getStats(int & tcount2, int & tmem2)                         //  get statistics
 
6709
{
 
6710
   tcount2 = tcount;
 
6711
   tmem2 = tmem;
 
6712
}
 
6713
 
 
6714
 
 
6715
void xstring::validate() const                                             //  validate integrity
 
6716
{
 
6717
   if (wmi != wmiv) appcrash("xstring bad wmi",null);
 
6718
   if (xmem < xcc+1) appcrash("xstring xmem < xcc+1",null);
 
6719
   if (xcc != (int) strlen(xpp)) appcrash("xstring xcc != strlen(xpp)",null);
 
6720
}
 
6721
 
 
6722
 
 
6723
/**************************************************************************
 
6724
      Vxstring class (array or vector of xstring)
 
6725
***************************************************************************/
 
6726
 
 
6727
Vxstring::Vxstring(int ii)                                                 //  constructor
 
6728
{  
 
6729
   pdata = 0;
 
6730
   nd = ii;
 
6731
   if (nd) pdata = new xstring[nd];
 
6732
   if (nd && !pdata) appcrash("Vxstring NEW fail",null);
 
6733
}
 
6734
 
 
6735
 
 
6736
Vxstring::~Vxstring()                                                      //  destructor
 
6737
{
 
6738
   if (nd) delete[] pdata;
 
6739
   pdata = 0;
 
6740
   nd = 0;
 
6741
}
 
6742
 
 
6743
 
 
6744
Vxstring::Vxstring(const Vxstring & pold)                                  //  copy constructor
 
6745
{
 
6746
   pdata = 0;
 
6747
   nd = pold.nd;                                                           //  set size
 
6748
   if (nd) pdata = new xstring[nd];                                        //  allocate memory
 
6749
   if (nd && !pdata) appcrash("Vxstring NEW fail");
 
6750
   for (int ii = 0; ii < nd; ii++) pdata[ii] = pold[ii];                   //  copy defined elements
 
6751
}
 
6752
 
 
6753
 
 
6754
Vxstring Vxstring::operator= (const Vxstring & vdstr)                      //  operator =
 
6755
{
 
6756
   if (nd) delete[] pdata;                                                 //  delete old memory
 
6757
   pdata = 0;
 
6758
   nd = vdstr.nd;
 
6759
   if (nd) pdata = new xstring[nd];                                        //  allocate new memory
 
6760
   if (nd && !pdata) appcrash("Vxstring NEW fail",null);
 
6761
   for (int ii = 0; ii < nd; ii++) pdata[ii] = vdstr.pdata[ii];            //  copy elements
 
6762
   return *this;
 
6763
}
 
6764
 
 
6765
 
 
6766
xstring & Vxstring::operator[] (int ii)                                    //  operator []
 
6767
{
 
6768
   static xstring xnull(0);
 
6769
   if (ii < nd) return pdata[ii];                                          //  return reference
 
6770
   appcrash("Vxstring index invalid %d %d",nd,ii,null);
 
6771
   return xnull;
 
6772
}
 
6773
 
 
6774
 
 
6775
const xstring & Vxstring::operator[] (int ii) const                        //  operator []
 
6776
{
 
6777
   static xstring xnull(0);
 
6778
   if (ii < nd) return pdata[ii];                                          //  return reference
 
6779
   appcrash("Vxstring index invalid %d %d",nd,ii,null);
 
6780
   return xnull;
 
6781
}
 
6782
 
 
6783
 
 
6784
int Vxstring::search(const char *string)                                   //  find element in unsorted Vxstring
 
6785
{
 
6786
   for (int ii = 0; ii < nd; ii++)
 
6787
        if (strEqu(pdata[ii],string)) return ii;
 
6788
   return -1;
 
6789
}
 
6790
 
 
6791
 
 
6792
int Vxstring::bsearch(const char *string)                                  //  find element in sorted Vxstring
 
6793
{                                                                          //   (binary search)
 
6794
   int   nn, ii, jj, kk, rkk;
 
6795
 
 
6796
   nn = nd;
 
6797
   if (! nn) return 0;                                                     //  empty list
 
6798
 
 
6799
   ii = nn / 2;                                                            //  next element to search
 
6800
   jj = (ii + 1) / 2;                                                      //  next increment
 
6801
   nn--;                                                                   //  last element
 
6802
   rkk = 0;
 
6803
 
 
6804
   while (1)
 
6805
   {
 
6806
      kk = strcmp(pdata[ii],string);                                       //  check element
 
6807
 
 
6808
      if (kk > 0) 
 
6809
      {
 
6810
         ii -= jj;                                                         //  too high, go down
 
6811
         if (ii < 0) return -1;
 
6812
      }
 
6813
 
 
6814
      else if (kk < 0) 
 
6815
      {
 
6816
         ii += jj;                                                         //  too low, go up
 
6817
         if (ii > nn) return -1;
 
6818
      }
 
6819
 
 
6820
      else if (kk == 0) return ii;                                         //  matched
 
6821
 
 
6822
      jj = jj / 2;                                                         //  reduce increment
 
6823
 
 
6824
      if (jj == 0) 
 
6825
      {
 
6826
         jj = 1;                                                           //  step by 1 element
 
6827
         if (! rkk) rkk = kk;                                              //  save direction
 
6828
         else 
 
6829
         {
 
6830
            if (rkk > 0) { if (kk < 0) return -1; }                        //  if change direction, fail
 
6831
            else if (kk > 0) return -1;
 
6832
         }
 
6833
      }
 
6834
   }
 
6835
}
 
6836
 
 
6837
 
 
6838
static int  VDsortKeys[10][3], VDsortNK;
 
6839
 
 
6840
int Vxstring::sort(int NK, int keys[][3])                                  //  sort elements by subfields
 
6841
{                                                                          //  key[ii][0] = position
 
6842
   int     NR, RL, ii;                                                     //         [1] = length
 
6843
   HeapSortUcomp  VDsortComp;                                              //         [2] = 1/2 = ascending/desc.
 
6844
                                                                           //             = 3/4 =  + ignore case
 
6845
   NR = nd;
 
6846
   if (NR < 2) return 1;
 
6847
 
 
6848
   RL = sizeof(xstring);
 
6849
 
 
6850
   if (NK < 1) appcrash("Vxstring::sort, bad NK",null);
 
6851
   if (NK > 10) appcrash("Vxstring::sort, bad NK",null);
 
6852
   VDsortNK = NK;
 
6853
 
 
6854
   for (ii = 0; ii < NK; ii++)
 
6855
   {
 
6856
      VDsortKeys[ii][0] = keys[ii][0];
 
6857
      VDsortKeys[ii][1] = keys[ii][1];
 
6858
      VDsortKeys[ii][2] = keys[ii][2];
 
6859
   }
 
6860
 
 
6861
   HeapSort((char *) pdata,RL,NR,VDsortComp);
 
6862
 
 
6863
   return 1;
 
6864
}
 
6865
 
 
6866
 
 
6867
int VDsortComp(const char *r1, const char *r2)
 
6868
{
 
6869
   xstring      *d1, *d2;
 
6870
   const char   *p1, *p2;
 
6871
   int          ii, stat, kpos, ktype, kleng;
 
6872
 
 
6873
   d1 = (xstring *) r1;
 
6874
   d2 = (xstring *) r2;
 
6875
   p1 = *d1;
 
6876
   p2 = *d2;
 
6877
 
 
6878
   for (ii = 0; ii < VDsortNK; ii++)                                       //  compare each key
 
6879
   {
 
6880
      kpos = VDsortKeys[ii][0];
 
6881
      kleng = VDsortKeys[ii][1];
 
6882
      ktype = VDsortKeys[ii][2];
 
6883
 
 
6884
      if (ktype == 1)
 
6885
      {
 
6886
         stat = strncmp(p1+kpos,p2+kpos,kleng);
 
6887
         if (stat) return stat;
 
6888
         continue;
 
6889
      }
 
6890
 
 
6891
      else if (ktype == 2)
 
6892
      {
 
6893
         stat = strncmp(p1+kpos,p2+kpos,kleng);
 
6894
         if (stat) return -stat;
 
6895
         continue;
 
6896
      }
 
6897
 
 
6898
      else if (ktype == 3)
 
6899
      {
 
6900
         stat = strncasecmp(p1+kpos,p2+kpos,kleng);
 
6901
         if (stat) return stat;
 
6902
         continue;
 
6903
      }
 
6904
 
 
6905
      else if (ktype == 4)
 
6906
      {
 
6907
         stat = strncasecmp(p1+kpos,p2+kpos,kleng);
 
6908
         if (stat) return -stat;
 
6909
         continue;
 
6910
      }
 
6911
 
 
6912
      appcrash("Vxstring::sort, bad KEYS sort type",null);
 
6913
   }
 
6914
 
 
6915
   return 0;
 
6916
}
 
6917
 
 
6918
 
 
6919
int Vxstring::sort(int pos, int cc)                                        //  sort elements ascending
 
6920
{
 
6921
   int   key[3];
 
6922
 
 
6923
   if (! cc) cc = 999999;
 
6924
   key[0] = pos;
 
6925
   key[1] = cc;
 
6926
   key[2] = 1;
 
6927
 
 
6928
   sort(1,&key);
 
6929
 
 
6930
   return 1;
 
6931
}
 
6932
 
 
6933
 
 
6934
/**************************************************************************
 
6935
     Hash Table class
 
6936
***************************************************************************/
 
6937
 
 
6938
//  static members (robust for tables up to 60% full)
 
6939
 
 
6940
int HashTab::trys1 = 100;                                                  //  Add() tries
 
6941
int HashTab::trys2 = 200;                                                  //  Find() tries
 
6942
 
 
6943
 
 
6944
HashTab::HashTab(int _cc, int _cap)                                        //  constructor
 
6945
{
 
6946
   cc = 4 * (_cc + 4) / 4;                                                 //  + 1 + mod 4 length
 
6947
   cap = _cap;
 
6948
   int len = cc * cap;
 
6949
   table = new char [len];
 
6950
   if (! table) appcrash("HashTab() new %d fail",len,null);
 
6951
   memset(table,0,len);
 
6952
}
 
6953
 
 
6954
 
 
6955
HashTab::~HashTab()                                                        //  destructor
 
6956
{
 
6957
   delete [] table;
 
6958
   table = 0;
 
6959
}
 
6960
 
 
6961
 
 
6962
//  Add a new string to table
 
6963
 
 
6964
int HashTab::Add(const char *string)
 
6965
{
 
6966
   int   pos, fpos, trys;
 
6967
 
 
6968
   pos = strHash(string,cap);                                              //  get random position
 
6969
   pos = pos * cc;
 
6970
 
 
6971
   for (trys = 0, fpos = -1;                                               //  find next free slot 
 
6972
        trys < trys1;                                                      //   at/after position
 
6973
        trys++, pos += cc)
 
6974
   {
 
6975
      if (pos >= cap * cc) pos = 0;                                        //  last position wraps to 1st
 
6976
 
 
6977
      if (! table[pos])                                                    //  empty slot: string not found
 
6978
      {
 
6979
         if (fpos != -1) pos = fpos;                                       //  use prior deleted slot if there
 
6980
         strncpy(table+pos,string,cc);                                     //  insert new string
 
6981
         table[pos+cc-1] = 0;                                              //  insure null terminator
 
6982
         return (pos/cc);                                                  //  return rel. table entry
 
6983
      }
 
6984
 
 
6985
      if (table[pos] == -1)                                                //  deleted slot
 
6986
      {
 
6987
         if (fpos == -1) fpos = pos;                                       //  remember 1st one found
 
6988
         continue;
 
6989
      }
 
6990
 
 
6991
      if (strEqu(string,table+pos)) return -2;                             //  string already present
 
6992
   }   
 
6993
 
 
6994
   return -3;                                                              //  table full (trys1 exceeded)
 
6995
}
 
6996
 
 
6997
 
 
6998
//  Delete a string from table
 
6999
 
 
7000
int HashTab::Del(const char *string)
 
7001
{
 
7002
   int   pos, trys;
 
7003
 
 
7004
   pos = strHash(string,cap);                                              //  get random position
 
7005
   pos = pos * cc;
 
7006
 
 
7007
   for (trys = 0;                                                          //  search for string
 
7008
        trys < trys2;                                                      //   at/after position
 
7009
        trys++, pos += cc)
 
7010
   {
 
7011
      if (pos >= cap * cc) pos = 0;                                        //  last position wraps to 1st
 
7012
 
 
7013
      if (! table[pos]) return -1;                                         //  empty slot, string not found
 
7014
 
 
7015
      if (strEqu(string,table+pos))                                        //  string found
 
7016
      {
 
7017
         table[pos] = -1;                                                  //  delete table entry
 
7018
         return (pos/cc);                                                  //  return rel. table entry
 
7019
      }
 
7020
   }   
 
7021
 
 
7022
   appcrash("HashTab::Del() bug",null);                                    //  exceed trys2, must not happen
 
7023
   return 0;                                                               //  (table too full to function)
 
7024
}
 
7025
 
 
7026
 
 
7027
//  Find a table entry.
 
7028
 
 
7029
int HashTab::Find(const char *string)
 
7030
{
 
7031
   int   pos, trys;
 
7032
 
 
7033
   pos = strHash(string,cap);                                              //  get random position
 
7034
   pos = pos * cc;
 
7035
 
 
7036
   for (trys = 0;                                                          //  search for string
 
7037
        trys < trys2;                                                      //   at/after position
 
7038
        trys++, pos += cc)
 
7039
   {
 
7040
      if (pos >= cap * cc) pos = 0;                                        //  last position wraps to 1st
 
7041
      if (! table[pos]) return -1;                                         //  empty slot, string not found
 
7042
      if (strEqu(string,table+pos)) return (pos/cc);                       //  string found, return rel. entry
 
7043
   }   
 
7044
 
 
7045
   appcrash("HashTab::Find() bug",null);                                   //  cannot happen
 
7046
   return 0;
 
7047
}
 
7048
 
 
7049
 
 
7050
//  return first or next table entry
 
7051
 
 
7052
int HashTab::GetNext(int & ftf, char *string)
 
7053
{
 
7054
   static int    pos;
 
7055
 
 
7056
   if (ftf)                                                                //  initial call
 
7057
   {
 
7058
      pos = 0;
 
7059
      ftf = 0;
 
7060
   }
 
7061
 
 
7062
   while (pos < (cap * cc))
 
7063
   {
 
7064
      if ((table[pos] == 0) || (table[pos] == -1))
 
7065
      {
 
7066
         pos += cc;
 
7067
         continue;
 
7068
      }
 
7069
 
 
7070
      strcpy(string,table+pos);                                            //  return string
 
7071
      pos += cc;
 
7072
      return 1;
 
7073
   }
 
7074
 
 
7075
   return -4;                                                              //  EOF
 
7076
}
 
7077
 
 
7078
 
 
7079
int HashTab::Dump()
 
7080
{
 
7081
   int   ii, pos;
 
7082
 
 
7083
   for (ii = 0; ii < cap; ii++)
 
7084
   {
 
7085
      pos = ii * cc;
 
7086
      if (table[pos] > 0) printf("%d %s \n", pos, table + pos);
 
7087
      if (table[pos] == -1) printf("%d deleted \n", pos);
 
7088
   }        
 
7089
   return 1;
 
7090
}
 
7091
 
 
7092
 
 
7093
/**************************************************************************
 
7094
     class for queue of dynamic strings
 
7095
***************************************************************************/
 
7096
 
 
7097
Queue::Queue(int cap)                                                      //  constructor
 
7098
{
 
7099
   int   err;
 
7100
   
 
7101
   err = mutex_init(&qmutex, 0);                                           //  create mutex = queue lock
 
7102
   if (err) appcrash("Queue(), mutex init fail",null);
 
7103
 
 
7104
   qcap = cap;                                                             //  queue capacity
 
7105
   ent1 = entN = qcount = 0;                                               //  state = empty
 
7106
   vd = new Vxstring(qcap);                                                //  create vector of xstring's
 
7107
   if (! vd) appcrash("Queue(), NEW fail %d",cap,null);
 
7108
   strcpy(wmi,"queue");
 
7109
   return;
 
7110
}
 
7111
 
 
7112
 
 
7113
Queue::~Queue()                                                            //  destructor
 
7114
{
 
7115
   if (strNeq(wmi,"queue")) appcrash("~Queue wmi fail",null);
 
7116
   wmi[0] = 0;
 
7117
   mutex_destroy(&qmutex);                                                 //  destroy mutex
 
7118
   qcount = qcap = ent1 = entN = -1;
 
7119
   delete vd;
 
7120
   vd = 0;
 
7121
   return;
 
7122
}
 
7123
 
 
7124
 
 
7125
void Queue::lock()                                                         //  lock queue (private)
 
7126
{
 
7127
   int   err;
 
7128
   err = mutex_lock(&qmutex);                                              //  reserve mutex or suspend
 
7129
   if (err) appcrash("Queue mutex lock fail",null);
 
7130
   return;
 
7131
}
 
7132
 
 
7133
 
 
7134
void Queue::unlock()                                                       //  unlock queue (private)
 
7135
{
 
7136
   int   err;
 
7137
   err = mutex_unlock(&qmutex);                                            //  release mutex
 
7138
   if (err) appcrash("Queue mutex unlock fail",null);
 
7139
   return;
 
7140
}
 
7141
 
 
7142
 
 
7143
int Queue::getCount()                                                      //  get current entry count
 
7144
{
 
7145
   if (strNeq(wmi,"queue")) appcrash("Queue getCount wmi fail",null);
 
7146
   return qcount;
 
7147
}
 
7148
 
 
7149
 
 
7150
int Queue::push(const xstring *newEnt, double wait)                        //  add entry to queue, with max. wait
 
7151
{
 
7152
   double  elaps = 0.0;
 
7153
   int     count;
 
7154
   
 
7155
   if (strNeq(wmi,"queue")) appcrash("Queue::push wmi fail",null);
 
7156
 
 
7157
   lock();                                                                 //  lock queue
 
7158
   while (qcount == qcap) {                                                //  queue full
 
7159
      unlock();                                                            //  unlock queue
 
7160
      if (elaps >= wait) return -1;                                        //  too long, return -1 status
 
7161
      usleep(1000);                                                        //  sleep in 1 millisec. steps
 
7162
      elaps += 0.001;                                                      //  until queue not full
 
7163
      lock();                                                              //  lock queue
 
7164
   }
 
7165
 
 
7166
   (* vd)[entN] = *newEnt;                                                 //  copy new entry into queue
 
7167
   entN++;                                                                 //  incr. end pointer
 
7168
   if (entN == qcap) entN = 0;
 
7169
   qcount++;                                                               //  incr. queue count
 
7170
   count = qcount;
 
7171
   unlock();                                                               //  unlock queue
 
7172
   return count;                                                           //  return curr. queue count
 
7173
}
 
7174
 
 
7175
 
 
7176
xstring *Queue::pop1()                                                     //  get 1st (oldest) entry and remove
 
7177
{
 
7178
   xstring    *entry;
 
7179
   
 
7180
   if (strNeq(wmi,"queue")) appcrash("Queue::pop1 wmi fail",null);
 
7181
 
 
7182
   lock();                                                                 //  lock queue
 
7183
 
 
7184
   if (qcount == 0) entry = 0;                                             //  queue empty
 
7185
   else {
 
7186
      entry = &(* vd)[ent1];                                               //  get first entry
 
7187
      ent1++;                                                              //  index pointer to next
 
7188
      if (ent1 == qcap) ent1 = 0;
 
7189
      qcount--;                                                            //  decr. queue count
 
7190
   }
 
7191
 
 
7192
   unlock();                                                               //  unlock queue
 
7193
   return entry;
 
7194
}
 
7195
 
 
7196
 
 
7197
xstring *Queue::popN()                                                     //  get last (newest) entry and remove
 
7198
{
 
7199
   xstring   *entry;
 
7200
   
 
7201
   if (strNeq(wmi,"queue")) appcrash("Queue::popN wmi fail",null);
 
7202
 
 
7203
   lock();                                                                 //  lock queue
 
7204
 
 
7205
   if (qcount == 0) entry = 0;                                             //  queue empty
 
7206
   else {
 
7207
      if (entN == 0) entN = qcap;                                          //  index pointer to prior
 
7208
      entN--;
 
7209
      qcount--;                                                            //  decr. queue count
 
7210
      entry = &(* vd)[entN];                                               //  get last entry
 
7211
   }
 
7212
 
 
7213
   unlock();                                                               //  unlock queue
 
7214
   return entry;
 
7215
}
 
7216
 
 
7217
 
 
7218
/**************************************************************************
 
7219
 
 
7220
   Tree class, tree-structured data storage without limits
 
7221
 
 
7222
   Store any amount of data at any depth within a tree-structure with named nodes.
 
7223
   Data can be found using an ordered list of node names or node numbers.
 
7224
 
 
7225
   Node numbers are in the sequence added using put() with names,
 
7226
   or the same as those numbers used in put() with numbers.
 
7227
 
 
7228
   Internal code conventions: 
 
7229
      - caller level is node 0, next level is node 1, etc.
 
7230
      - node names and numbers in calls to get() and put() refer to next levels
 
7231
      - number of levels = 1+nn, where nn is max. in calls to put(...nodes[], nn)
 
7232
 
 
7233
***************************************************************************/
 
7234
 
 
7235
#define wmid 1374602859
 
7236
 
 
7237
 
 
7238
//  constructor
 
7239
 
 
7240
Tree::Tree(const char *name)
 
7241
{
 
7242
   wmi = wmid;
 
7243
   tname = 0;
 
7244
   tmem = 0;
 
7245
   tdata = 0;
 
7246
   nsub = 0;
 
7247
   psub = 0;
 
7248
 
 
7249
   if (name) 
 
7250
   {
 
7251
      int cc = strlen(name);
 
7252
      tname = new char[cc+1];
 
7253
      if (! tname) appcrash("Tree, no memory",null);
 
7254
      strcpy(tname,name);
 
7255
   }
 
7256
}
 
7257
 
 
7258
 
 
7259
//  destructor
 
7260
 
 
7261
Tree::~Tree()
 
7262
{
 
7263
   if (wmi != wmid) appcrash("not a Tree",null);
 
7264
   if (tname) delete [] tname;
 
7265
   tname = 0;
 
7266
   if (tmem) free(tdata);
 
7267
   tmem = 0;
 
7268
   tdata = 0;
 
7269
   for (int ii = 0; ii < nsub; ii++) delete psub[ii];
 
7270
   if (psub) free(psub);
 
7271
   nsub = 0;
 
7272
   psub = 0;
 
7273
}
 
7274
 
 
7275
 
 
7276
//  put data by node names[]
 
7277
 
 
7278
int Tree::put(void *data, int dd, char *nodes[], int nn)
 
7279
{
 
7280
   Tree    *tnode;
 
7281
   
 
7282
   if (wmi != wmid) appcrash("not a Tree",null);
 
7283
   tnode = make(nodes,nn);
 
7284
   if (tnode->tdata) free(tnode->tdata);
 
7285
   tnode->tdata = new char[dd];
 
7286
   if (! tnode->tdata) appcrash("Tree, no memory",null);
 
7287
   tnode->tmem = dd;
 
7288
   memmove(tnode->tdata,data,dd);
 
7289
   return 1;
 
7290
}
 
7291
 
 
7292
 
 
7293
//  put data by node numbers[]
 
7294
 
 
7295
int Tree::put(void *data, int dd, int nodes[], int nn)
 
7296
{
 
7297
   Tree    *tnode;
 
7298
   
 
7299
   if (wmi != wmid) appcrash("not a Tree",null);
 
7300
   tnode = make(nodes,nn);
 
7301
   if (tnode->tdata) free(tnode->tdata);
 
7302
   tnode->tdata = new char[dd];
 
7303
   if (! tnode->tdata) appcrash("Tree, no memory",null);
 
7304
   tnode->tmem = dd;
 
7305
   memmove(tnode->tdata,data,dd);
 
7306
   return 1;
 
7307
}
 
7308
 
 
7309
 
 
7310
//  get data by node names[]
 
7311
 
 
7312
int Tree::get(void *data, int dd, char *nodes[], int nn)
 
7313
{
 
7314
   Tree *tnode = find(nodes,nn);
 
7315
   if (! tnode) return 0;
 
7316
   if (! tnode->tmem) return 0;
 
7317
   if (dd > tnode->tmem) dd = tnode->tmem;
 
7318
   memmove(data,tnode->tdata,dd);
 
7319
   return dd;
 
7320
}
 
7321
 
 
7322
 
 
7323
//  get data by node numbers[]
 
7324
 
 
7325
int Tree::get(void *data, int dd, int nodes[], int nn)
 
7326
{
 
7327
   Tree *tnode = find(nodes,nn);
 
7328
   if (! tnode) return 0;
 
7329
   if (! tnode->tmem) return 0;
 
7330
   if (dd > tnode->tmem) dd = tnode->tmem;
 
7331
   memmove(data,tnode->tdata,dd);
 
7332
   return dd;
 
7333
}
 
7334
 
 
7335
 
 
7336
//  find a given node by names[]
 
7337
 
 
7338
Tree * Tree::find(char *nodes[], int nn)
 
7339
{
 
7340
   int      ii;
 
7341
   
 
7342
   for (ii = 0; ii < nsub; ii++)
 
7343
      if (psub[ii]->tname && strEqu(nodes[0],psub[ii]->tname)) break;
 
7344
   if (ii == nsub) return 0;
 
7345
   if (nn == 1) return psub[ii];
 
7346
   return psub[ii]->find(&nodes[1],nn-1);
 
7347
}
 
7348
 
 
7349
 
 
7350
//  find a given node by numbers[]
 
7351
 
 
7352
Tree * Tree::find(int nodes[], int nn)
 
7353
{
 
7354
   int ii = nodes[0];
 
7355
   if (ii >= nsub) return 0;
 
7356
   if (! psub[ii]) return 0;
 
7357
   if (nn == 1) return psub[ii];
 
7358
   return psub[ii]->find(&nodes[1],nn-1);
 
7359
}
 
7360
 
 
7361
 
 
7362
//  find or create a given node by names[]
 
7363
 
 
7364
Tree * Tree::make(char *nodes[], int nn)
 
7365
{
 
7366
   int      ii;
 
7367
   Tree   **psub2;
 
7368
   
 
7369
   for (ii = 0; ii < nsub; ii++)
 
7370
      if (psub[ii]->tname && strEqu(nodes[0],psub[ii]->tname)) break;
 
7371
 
 
7372
   if (ii == nsub)
 
7373
   {
 
7374
      psub2 = new Tree * [nsub+1];
 
7375
      if (! psub2) appcrash("Tree, no memory",null);
 
7376
      for (ii = 0; ii < nsub; ii++) psub2[ii] = psub[ii];
 
7377
      delete [] psub;
 
7378
      psub = psub2;
 
7379
      nsub++;
 
7380
      psub[ii] = new Tree(nodes[0]);
 
7381
      if (! psub[ii]) appcrash("Tree, no memory",null);
 
7382
   }
 
7383
 
 
7384
   if (nn == 1) return psub[ii];
 
7385
   return psub[ii]->make(&nodes[1],nn-1);
 
7386
}
 
7387
 
 
7388
 
 
7389
//  find or create a given node by numbers[]
 
7390
 
 
7391
Tree * Tree::make(int nodes[], int nn)
 
7392
{
 
7393
   Tree   **psub2;
 
7394
   int      ii, jj;
 
7395
 
 
7396
   ii = nodes[0];
 
7397
   if ((ii < nsub) && psub[ii])
 
7398
   {
 
7399
      if (nn == 1) return psub[ii];
 
7400
      return psub[ii]->make(&nodes[1],nn-1);
 
7401
   }
 
7402
 
 
7403
   if (ii >= nsub)
 
7404
   {
 
7405
      psub2 = new Tree * [ii+1];
 
7406
      if (psub2 == null) appcrash("Tree, no memory",null);
 
7407
      for (jj = 0; jj < nsub; jj++) psub2[jj] = psub[jj];
 
7408
      for (jj = nsub; jj < ii; jj++) psub2[jj] = 0;
 
7409
      delete [] psub;
 
7410
      psub = psub2;
 
7411
      nsub = ii + 1;
 
7412
   }
 
7413
 
 
7414
   psub[ii] = new Tree("noname");
 
7415
   if (! psub[ii]) appcrash("Tree, no memory",null);
 
7416
 
 
7417
   if (nn == 1) return psub[ii];
 
7418
   return psub[ii]->make(&nodes[1],nn-1);
 
7419
}
 
7420
 
 
7421
 
 
7422
//  dump tree data to stdout (call with level 0)
 
7423
 
 
7424
void Tree::dump(int level)
 
7425
{
 
7426
   const char    *name;
 
7427
   
 
7428
   if (! tname) name = "noname";
 
7429
   else name = tname;
 
7430
   printf("%*s level: %d  name: %s  subs: %d  mem: %d \n",
 
7431
                        level*2,"",level,name,nsub,tmem);
 
7432
   for (int ii = 0; ii < nsub; ii++) 
 
7433
         if (psub[ii]) psub[ii]->dump(level+1);
 
7434
}
 
7435
 
 
7436
 
 
7437
//  get node counts and total data per level
 
7438
//  level 0 + nn more levels, as given in calls to put(...nodes[],nn)
 
7439
//  caller must initialize counters to zero
 
7440
 
 
7441
void Tree::stats(int nn[], int nd[])
 
7442
{
 
7443
   nn[0] += 1;
 
7444
   nd[0] += tmem;
 
7445
   for (int ii = 0; ii < nsub; ii++) 
 
7446
      if (psub[ii]) psub[ii]->stats(&nn[1],&nd[1]);
 
7447
}
 
7448
 
 
7449
 
 
7450