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.

493 lines
10KB

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