Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

511 lines
9.9KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2008 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. #define _LOGGABLE_C
  19. #include "Loggable.H"
  20. #undef _LOGABLE_C
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <stdarg.h>
  24. #include <string.h>
  25. FILE *Loggable::_fp;
  26. int Loggable::_log_id = 0;
  27. int Loggable::_level = 0;
  28. int Loggable::_undo_index = 1;
  29. vector <Loggable *> Loggable::_loggables;
  30. map <string, create_func*> Loggable::_class_map;
  31. queue <char *> Loggable::_transaction;
  32. bool
  33. Loggable::open ( const char *filename )
  34. {
  35. if ( ! ( Loggable::_fp = fopen( filename, "a+" ) ) )
  36. {
  37. printf( "Could not open log file for writing!" );
  38. return false;
  39. }
  40. /* TODO: replay log here */
  41. /* FIXME: handle transactions!!! */
  42. {
  43. char buf[BUFSIZ];
  44. while ( fscanf( _fp, "%[^\n]\n", buf ) == 1 )
  45. {
  46. do_this( buf, false );
  47. }
  48. }
  49. return true;
  50. }
  51. /** sigh. parse a string of ":name value :name value" pairs into an
  52. * array of strings, one per pair */
  53. // FIXME: doesn't handle the case of :name ":foo bar", nested quotes
  54. // or other things it should. Also, quotes should be removed here, not
  55. // in client code.
  56. static
  57. char **
  58. parse_alist( const char *s )
  59. {
  60. // FIXME: bogus over allocation...
  61. int tl = strlen( s );
  62. char **r = (char**)malloc( sizeof( char* ) * tl );
  63. const char *e = s + tl;
  64. const char *c = NULL;
  65. int i = 0;
  66. for ( ; ; s++ )
  67. {
  68. /* if ( *s == '\n' ) */
  69. /* break; */
  70. // if ( *s == ':' || s == e )
  71. if ( *s == ':' || *s == '\0' )
  72. {
  73. if ( c )
  74. {
  75. int l = s - c;
  76. char *pair = (char*)malloc( l + 1 );
  77. strncpy( pair, c, l );
  78. pair[ l ] = '\0';
  79. r[ i++ ] = pair;
  80. }
  81. c = s;
  82. if ( *s == '\0' )
  83. break;
  84. }
  85. }
  86. r[ i ] = NULL;
  87. return r;
  88. }
  89. static
  90. void free_sa ( char **sa )
  91. {
  92. char **a = sa;
  93. for ( ; *a; a++ )
  94. free( *a );
  95. free( sa );
  96. }
  97. /** 'do' a message like "Region 0xF1 set :r 123" */
  98. bool
  99. Loggable::do_this ( const char *s, bool reverse )
  100. {
  101. int id;
  102. if ( ! ( sscanf( s, "%*s %X ", &id ) > 0 ) )
  103. return false;
  104. Loggable *l = find( id );
  105. // assert( l );
  106. char classname[40];
  107. char command[40];
  108. char *arguments;
  109. const char *create, *destroy;
  110. if ( reverse )
  111. {
  112. sscanf( s, "%s %*X %s %*[^\n<] %a[^\n]", classname, command, &arguments );
  113. create = "destroy";
  114. destroy = "create";
  115. }
  116. else
  117. {
  118. sscanf( s, "%s %*X %s %a[^\n<]", classname, command, &arguments );
  119. create = "create";
  120. destroy = "destroy";
  121. }
  122. if ( ! strcmp( command, destroy ) )
  123. {
  124. int id = l->id();
  125. delete l;
  126. _loggables[ id ] = NULL;
  127. }
  128. else if ( ! strcmp( command, "set" ) )
  129. {
  130. printf( "got set command.\n" );
  131. char **sa = parse_alist( arguments );
  132. l->log_start();
  133. l->set( sa );
  134. l->log_end();
  135. }
  136. else if ( ! strcmp( command, create ) )
  137. {
  138. char **sa = parse_alist( arguments );
  139. if ( ! _class_map[ string( classname ) ] )
  140. printf( "error class %s is unregistered!\n", classname );
  141. else
  142. {
  143. /* create */
  144. Loggable *l = _class_map[ string( classname ) ]( sa );
  145. l->update_id( id );
  146. l->log_create();
  147. }
  148. }
  149. }
  150. void
  151. Loggable::undo ( void )
  152. {
  153. char *buf = new char[ BUFSIZ ];
  154. // fflush( _fp );
  155. fseek( _fp, 0, SEEK_END );
  156. size_t len = ftell( _fp );
  157. fseek( _fp, 0 - (BUFSIZ > len ? len : BUFSIZ), SEEK_END );
  158. len = fread( buf, 1, BUFSIZ, _fp );
  159. char *s = buf + len - 1;
  160. // FIXME: handle blocks
  161. int i = 1;
  162. /* move back _undo_index items from the end */
  163. for ( int j = _undo_index; j-- ; )
  164. for ( --s; *s && s >= buf; --s, ++i )
  165. {
  166. if ( *s == '\n' )
  167. {
  168. if ( *(s + 1) == '\t' )
  169. continue;
  170. if ( *(s + 1) == '}' )
  171. {
  172. *(s + 1) = NULL;
  173. continue;
  174. }
  175. break;
  176. }
  177. }
  178. s++;
  179. buf[ len ] = NULL;
  180. if ( ! strlen( s ) )
  181. {
  182. printf( "corrupt undo file or no undo entries.\n" );
  183. return;
  184. }
  185. char *b = s;
  186. s += strlen( s ) - 1;
  187. if ( strtok( b, "\n" ) == NULL )
  188. {
  189. printf( "error, empty undo transaction!\n" );
  190. abort();
  191. }
  192. int n = 1;
  193. while ( strtok( NULL, "\n" ) )
  194. ++n;
  195. int ui = _undo_index;
  196. block_start();
  197. if ( strcmp( b, "{" ) )
  198. {
  199. /* It's a single undo, get rid of trailing messages in this block */
  200. n = 1;
  201. s = b + 2;
  202. s += strlen( s ) - 1;
  203. }
  204. while ( n-- )
  205. {
  206. while ( s >= b && *(--s) );
  207. s++;
  208. if ( ! strcmp( s, "{" ) )
  209. break;
  210. if ( *s == '\t' )
  211. s++;
  212. printf( "undoing \"%s\"\n", s );
  213. do_this( s, true );
  214. s -= 2;
  215. }
  216. block_end();
  217. // FIXME: bogus... needs to account for multiple events.
  218. _undo_index = ui + 1;
  219. ++_undo_index;
  220. delete buf;
  221. }
  222. /** write a snapshot of the state of all loggable objects, sufficient
  223. * for later reconstruction, to /file/ */
  224. bool
  225. Loggable::snapshot( const char *file )
  226. {
  227. FILE *ofp = _fp;
  228. if ( ! ( _fp = fopen( file, "a" ) ) )
  229. {
  230. _fp = ofp;
  231. return false;
  232. }
  233. for ( int i = 0; i < _log_id; ++i )
  234. {
  235. _loggables[ i ]->log_create();
  236. }
  237. fclose( _fp );
  238. _fp = ofp;
  239. return true;
  240. }
  241. void
  242. Loggable::log ( const char *fmt, ... )
  243. {
  244. /* FIXME: bogus limit */
  245. static char buf[1024];
  246. static int i = 0;
  247. va_list args;
  248. if ( fmt )
  249. {
  250. va_start( args, fmt );
  251. i += vsprintf( buf + i, fmt, args );
  252. va_end( args );
  253. }
  254. if ( rindex( buf, '\n' ) )
  255. {
  256. _transaction.push( strdup( buf ) );
  257. i = 0;
  258. }
  259. }
  260. void
  261. Loggable::flush ( void )
  262. {
  263. if ( ! _fp )
  264. {
  265. printf( "error: no log file open!\n" );
  266. while ( ! _transaction.empty() )
  267. {
  268. free( _transaction.front() );
  269. _transaction.pop();
  270. }
  271. return;
  272. }
  273. int n = _transaction.size();
  274. if ( n > 1 )
  275. fprintf( _fp, "{\n" );
  276. while ( ! _transaction.empty() )
  277. {
  278. char *s = _transaction.front();
  279. _transaction.pop();
  280. if ( n > 1 )
  281. fprintf( _fp, "\t" );
  282. fprintf( _fp, "%s", s );
  283. free( s );
  284. }
  285. if ( n > 1 )
  286. fprintf( _fp, "}\n" );
  287. fflush( _fp );
  288. }
  289. void
  290. Loggable::log_print( char **o, char **n )
  291. {
  292. if ( n )
  293. for ( ; *n; n++ )
  294. log( "%s%s", *n, *(n + 1) ? " " : "" );
  295. if ( o && *o )
  296. {
  297. if ( n ) log( " << " );
  298. for ( ; *o; o++ )
  299. log( "%s%s", *o, *(o + 1) ? " " : "" );
  300. }
  301. log( "\n" );
  302. }
  303. /** compare elements of dumps s1 and s2, removing those elements
  304. of dst which are not changed from src */
  305. static
  306. bool
  307. log_diff ( char **sa1, char **sa2 )
  308. {
  309. if ( ! sa1 )
  310. return true;
  311. int w = 0;
  312. for ( int i = 0; sa1[ i ]; ++i )
  313. {
  314. if ( ! strcmp( sa1[ i ], sa2[ i ] ) )
  315. {
  316. free( sa2[ i ] );
  317. free( sa1[ i ] );
  318. }
  319. else
  320. {
  321. sa2[ w ] = sa2[ i ];
  322. sa1[ w ] = sa1[ i ];
  323. w++;
  324. }
  325. }
  326. sa1[ w ] = NULL;
  327. sa2[ w ] = NULL;
  328. return w == 0 ? false : true;
  329. }
  330. void
  331. Loggable::log_start ( void )
  332. {
  333. if ( ! _old_state )
  334. _old_state = get();
  335. ++_nest;
  336. _undo_index = 1;
  337. }
  338. void
  339. Loggable::log_end ( void )
  340. {
  341. if ( --_nest > 0 )
  342. return;
  343. char **_new_state = get();
  344. if ( log_diff( _old_state, _new_state ) )
  345. {
  346. // indent();
  347. log( "%s 0x%X set ", class_name(), _id );
  348. log_print( _old_state, _new_state );
  349. }
  350. if ( _new_state )
  351. free_sa( _new_state );
  352. if ( _old_state )
  353. free_sa( _old_state );
  354. _old_state = NULL;
  355. if ( Loggable::_level == 0 )
  356. Loggable::flush();
  357. }
  358. void
  359. Loggable::log_create ( void )
  360. {
  361. // indent();
  362. log( "%s 0x%X create ", class_name(), _id );
  363. char **sa = get();
  364. if ( sa )
  365. {
  366. log_print( NULL, sa );
  367. free_sa( sa );
  368. }
  369. else
  370. log( "\n" );
  371. if ( Loggable::_level == 0 )
  372. Loggable::flush();
  373. }
  374. void
  375. Loggable::log_destroy ( void )
  376. {
  377. // indent();
  378. log( "%s 0x%X destroy (nothing) << ", class_name(), _id );
  379. char **sa = get();
  380. // log_print( sa, NULL );
  381. log_print( NULL, sa );
  382. free_sa( sa );
  383. if ( Loggable::_level == 0 )
  384. Loggable::flush();
  385. }