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.

539 lines
11KB

  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. return true;
  41. }
  42. /** sigh. parse a string of ":name value :name value" pairs into an array of strings, one per pair */
  43. // FIXME: doesn't handle the case of :name ":foo bar". Also, quotes should be removed here, not in client code.
  44. static
  45. char **
  46. parse_alist( const char *s )
  47. {
  48. // FIXME: bogus over allocation...
  49. int tl = strlen( s );
  50. char **r = (char**)malloc( sizeof( char* ) * tl );
  51. const char *e = s + tl;
  52. const char *c = NULL;
  53. int i = 0;
  54. for ( ; ; s++ )
  55. {
  56. /* if ( *s == '\n' ) */
  57. /* break; */
  58. // if ( *s == ':' || s == e )
  59. if ( *s == ':' || *s == '\0' )
  60. {
  61. if ( c )
  62. {
  63. int l = s - c;
  64. char *pair = (char*)malloc( l + 1 );
  65. strncpy( pair, c, l );
  66. pair[ l ] = '\0';
  67. r[ i++ ] = pair;
  68. }
  69. c = s;
  70. if ( *s == '\0' )
  71. break;
  72. }
  73. }
  74. r[ i ] = NULL;
  75. return r;
  76. }
  77. static
  78. void free_sa ( char **sa )
  79. {
  80. char **a = sa;
  81. for ( ; *a; a++ )
  82. free( *a );
  83. free( sa );
  84. }
  85. /* void */
  86. /* Loggable::redo ( const char *s ) */
  87. /* { */
  88. /* int id; */
  89. /* sscanf( s, "%*s %X ", &id ); */
  90. /* Loggable *l = find( id ); */
  91. /* // assert( l ); */
  92. /* char classname[40]; */
  93. /* char command[40]; */
  94. /* char *arguments; */
  95. /* sscanf( s, "%s %*X %s %*[^\n<] << %a[^\n]", classname, command, &arguments ); */
  96. /* int ui = _undo_index; */
  97. /* if ( ! l ) */
  98. /* { */
  99. /* printf( "corrupt undo?\n" ); */
  100. /* abort(); */
  101. /* } */
  102. /* if ( ! strcmp( command, "destroy" ) ) */
  103. /* { */
  104. /* char **sa = parse_alist( arguments ); */
  105. /* if ( ! _class_map[ string( classname ) ] ) */
  106. /* printf( "error class %s is unregistered!\n", classname ); */
  107. /* else */
  108. /* { */
  109. /* /\* create *\/ */
  110. /* Loggable *l = _class_map[ string( classname ) ]( sa ); */
  111. /* l->update_id( id ); */
  112. /* } */
  113. /* } */
  114. /* else */
  115. /* if ( ! strcmp( command, "set" ) ) */
  116. /* { */
  117. /* printf( "got set command.\n" ); */
  118. /* char **sa = parse_alist( arguments ); */
  119. /* l->log_start(); */
  120. /* l->set( sa ); */
  121. /* l->log_end(); */
  122. /* } */
  123. /* else */
  124. /* if ( ! strcmp( command, "create" ) ) */
  125. /* { */
  126. /* int id = l->id(); */
  127. /* delete l; */
  128. /* _loggables[ id ] = NULL; */
  129. /* } */
  130. /* } */
  131. void
  132. Loggable::undo ( void )
  133. {
  134. char *buf = new char[ BUFSIZ ];
  135. // fflush( _fp );
  136. fseek( _fp, 0, SEEK_END );
  137. size_t len = ftell( _fp );
  138. fseek( _fp, 0 - (BUFSIZ > len ? len : BUFSIZ), SEEK_END );
  139. len = fread( buf, 1, BUFSIZ, _fp );
  140. char *s = buf + len - 1;
  141. // FIXME: handle blocks
  142. int i = 1;
  143. /* move back _undo_index items from the end */
  144. for ( int j = _undo_index; j-- ; )
  145. for ( --s; *s && s >= buf; --s, ++i )
  146. {
  147. if ( *s == '\n' )
  148. {
  149. if ( *(s + 1) == '\t' )
  150. continue;
  151. if ( *(s + 1) == '}' )
  152. {
  153. *(s + 1) = NULL;
  154. continue;
  155. }
  156. break;
  157. }
  158. }
  159. s++;
  160. buf[ len ] = NULL;
  161. // fsync( fileno( _fp ) );
  162. /* pop the entry off the end */
  163. /* fseek( _fp, 0 - i, SEEK_END ); */
  164. /* ftruncate( fileno( _fp ), ftell( _fp ) ); */
  165. if ( ! strlen( s ) )
  166. {
  167. printf( "corrupt undo file or no undo entries.\n" );
  168. return;
  169. }
  170. char *b = s;
  171. s += strlen( s ) - 1;
  172. if ( strtok( b, "\n" ) == NULL )
  173. {
  174. printf( "error, empty undo transaction!\n" );
  175. abort();
  176. }
  177. int n = 1;
  178. while ( strtok( NULL, "\n" ) )
  179. ++n;
  180. int ui = _undo_index;
  181. block_start();
  182. if ( strcmp( b, "{" ) )
  183. {
  184. /* It's a single undo, get rid of trailing messages in this block */
  185. n = 1;
  186. s = b + 2;
  187. s += strlen( s ) - 1;
  188. }
  189. while ( n-- )
  190. {
  191. while ( s >= b && *(--s) );
  192. s++;
  193. if ( ! strcmp( s, "{" ) )
  194. break;
  195. if ( *s == '\t' )
  196. s++;
  197. printf( "undoing \"%s\"\n", s );
  198. int id;
  199. sscanf( s, "%*s %X ", &id );
  200. Loggable *l = find( id );
  201. // assert( l );
  202. char classname[40];
  203. char command[40];
  204. char *arguments;
  205. sscanf( s, "%s %*X %s %*[^\n<] << %a[^\n]", classname, command, &arguments );
  206. /* if ( ! l ) */
  207. /* { */
  208. /* printf( "corrupt undo?\n" ); */
  209. /* abort(); */
  210. /* } */
  211. if ( ! strcmp( command, "destroy" ) )
  212. {
  213. char **sa = parse_alist( arguments );
  214. if ( ! _class_map[ string( classname ) ] )
  215. printf( "error class %s is unregistered!\n", classname );
  216. else
  217. {
  218. /* create */
  219. Loggable *l = _class_map[ string( classname ) ]( sa );
  220. l->update_id( id );
  221. l->log_create();
  222. }
  223. }
  224. else
  225. if ( ! strcmp( command, "set" ) )
  226. {
  227. printf( "got set command.\n" );
  228. char **sa = parse_alist( arguments );
  229. l->log_start();
  230. l->set( sa );
  231. l->log_end();
  232. }
  233. else
  234. if ( ! strcmp( command, "create" ) )
  235. {
  236. int id = l->id();
  237. delete l;
  238. _loggables[ id ] = NULL;
  239. }
  240. s -= 2;
  241. }
  242. block_end();
  243. // FIXME: bogus... needs to account for multiple events.
  244. _undo_index = ui + 1;
  245. ++_undo_index;
  246. delete buf;
  247. }
  248. /** write a snapshot of the state of all loggable objects, sufficient
  249. * for later reconstruction, to /file/ */
  250. bool
  251. Loggable::snapshot( const char *file )
  252. {
  253. FILE *ofp = _fp;
  254. if ( ! ( _fp = fopen( file, "a" ) ) )
  255. {
  256. _fp = ofp;
  257. return false;
  258. }
  259. for ( int i = 0; i < _log_id; ++i )
  260. {
  261. _loggables[ i ]->log_create();
  262. }
  263. fclose( _fp );
  264. _fp = ofp;
  265. return true;
  266. }
  267. void
  268. Loggable::log ( const char *fmt, ... )
  269. {
  270. /* FIXME: bogus limit */
  271. static char buf[1024];
  272. static int i = 0;
  273. va_list args;
  274. if ( fmt )
  275. {
  276. va_start( args, fmt );
  277. i += vsprintf( buf + i, fmt, args );
  278. va_end( args );
  279. }
  280. if ( rindex( buf, '\n' ) )
  281. {
  282. _transaction.push( strdup( buf ) );
  283. i = 0;
  284. }
  285. }
  286. void
  287. Loggable::flush ( void )
  288. {
  289. int n = _transaction.size();
  290. if ( n > 1 )
  291. fprintf( _fp, "{\n" );
  292. while ( ! _transaction.empty() )
  293. {
  294. char *s = _transaction.front();
  295. _transaction.pop();
  296. if ( n > 1 )
  297. fprintf( _fp, "\t" );
  298. fprintf( _fp, "%s", s );
  299. free( s );
  300. }
  301. if ( n > 1 )
  302. fprintf( _fp, "}\n" );
  303. fflush( _fp );
  304. }
  305. void
  306. Loggable::log_print( char **o, char **n )
  307. {
  308. if ( n )
  309. for ( ; *n; n++ )
  310. log( "%s%s", *n, *(n + 1) ? " " : "" );
  311. if ( o && *o )
  312. {
  313. if ( n ) log( " << " );
  314. for ( ; *o; o++ )
  315. log( "%s%s", *o, *(o + 1) ? " " : "" );
  316. }
  317. log( "\n" );
  318. }
  319. /** compare elements of dumps s1 and s2, removing those elements
  320. of dst which are not changed from src */
  321. static
  322. bool
  323. log_diff ( char **sa1, char **sa2 )
  324. {
  325. if ( ! sa1 )
  326. return true;
  327. int w = 0;
  328. for ( int i = 0; sa1[ i ]; ++i )
  329. {
  330. if ( ! strcmp( sa1[ i ], sa2[ i ] ) )
  331. {
  332. free( sa2[ i ] );
  333. free( sa1[ i ] );
  334. }
  335. else
  336. {
  337. sa2[ w ] = sa2[ i ];
  338. sa1[ w ] = sa1[ i ];
  339. w++;
  340. }
  341. }
  342. sa1[ w ] = NULL;
  343. sa2[ w ] = NULL;
  344. return w == 0 ? false : true;
  345. }
  346. void
  347. Loggable::log_start ( void )
  348. {
  349. if ( ! _old_state )
  350. _old_state = get();
  351. ++_nest;
  352. _undo_index = 1;
  353. }
  354. void
  355. Loggable::log_end ( void )
  356. {
  357. if ( --_nest > 0 )
  358. return;
  359. char **_new_state = get();
  360. if ( log_diff( _old_state, _new_state ) )
  361. {
  362. // indent();
  363. log( "%s 0x%X set ", class_name(), _id );
  364. log_print( _old_state, _new_state );
  365. }
  366. if ( _new_state )
  367. free_sa( _new_state );
  368. if ( _old_state )
  369. free_sa( _old_state );
  370. _old_state = NULL;
  371. if ( Loggable::_level == 0 )
  372. Loggable::flush();
  373. }
  374. void
  375. Loggable::log_create ( void )
  376. {
  377. // indent();
  378. log( "%s 0x%X create ", class_name(), _id );
  379. char **sa = get();
  380. if ( sa )
  381. {
  382. log_print( NULL, sa );
  383. free_sa( sa );
  384. }
  385. else
  386. log( "\n" );
  387. if ( Loggable::_level == 0 )
  388. Loggable::flush();
  389. }
  390. void
  391. Loggable::log_destroy ( void )
  392. {
  393. // indent();
  394. log( "%s 0x%X destroy (nothing) << ", class_name(), _id );
  395. char **sa = get();
  396. // log_print( sa, NULL );
  397. log_print( NULL, sa );
  398. free_sa( sa );
  399. if ( Loggable::_level == 0 )
  400. Loggable::flush();
  401. }