svn merge -r 16593:16648 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender-staging.git] / source / blender / python / BPY_menus.c
1 /* 
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * This is a new part of Blender.
24  *
25  * Contributor(s): Willian P. Germano, Michael Reimpell
26  *
27  * ***** END GPL LICENSE BLOCK *****
28 */
29
30 /* 
31  *This is the main file responsible for having bpython scripts accessible
32  * from Blender menus.  To know more, please start with its header file.
33  */
34
35 #include "BPY_menus.h"
36
37 #include <Python.h>
38 #ifndef WIN32
39   #include <dirent.h>
40 #else
41   #include "BLI_winstuff.h"
42 #endif
43 #include "BKE_global.h"
44 #include "BKE_utildefines.h"
45 #include "BIF_keyval.h"
46 #include "BLI_blenlib.h"
47 #include "MEM_guardedalloc.h"
48 #include "DNA_userdef_types.h"  /* for U.pythondir */
49 #include "api2_2x/EXPP_interface.h" /* for bpy_gethome() */
50
51 #define BPYMENU_DATAFILE "Bpymenus"
52 #define MAX_DIR_DEPTH 4 /* max depth for traversing scripts dirs */
53 #define MAX_DIR_NUMBER 30 /* max number of dirs in scripts dirs trees */
54
55 static int DEBUG;
56 static int Dir_Depth;
57 static int Dirs_Number;
58
59 /* BPyMenuTable holds all registered pymenus, as linked lists for each menu
60  * where they can appear (see PYMENUHOOKS enum in BPY_menus.h).
61 */
62 BPyMenu *BPyMenuTable[PYMENU_TOTAL];
63
64 static int bpymenu_group_atoi( char *str )
65 {
66         if( !strcmp( str, "Export" ) )
67                 return PYMENU_EXPORT;
68         else if( !strcmp( str, "Import" ) )
69                 return PYMENU_IMPORT;
70         else if( !strcmp( str, "Help" ) )
71                 return PYMENU_HELP;
72         else if( !strcmp( str, "HelpWebsites" ) )
73                 return PYMENU_HELPWEBSITES;
74         else if( !strcmp( str, "HelpSystem" ) )
75                 return PYMENU_HELPSYSTEM;
76         else if( !strcmp( str, "Render" ) )
77                 return PYMENU_RENDER;
78         else if( !strcmp( str, "System" ) )
79                 return PYMENU_SYSTEM;
80         else if( !strcmp( str, "Object" ) )
81                 return PYMENU_OBJECT;
82         else if( !strcmp( str, "Mesh" ) )
83                 return PYMENU_MESH;
84         else if( !strncmp( str, "Theme", 5 ) )
85                 return PYMENU_THEMES;
86         else if( !strcmp( str, "Add" ) )
87                 return PYMENU_ADD;
88         else if( !strcmp( str, "Wizards" ) )
89                 return PYMENU_WIZARDS;
90         else if( !strcmp( str, "Animation" ) )
91                 return PYMENU_ANIMATION;
92         else if( !strcmp( str, "Materials" ) )
93                 return PYMENU_MATERIALS;
94         else if( !strcmp( str, "UV" ) )
95                 return PYMENU_UV;
96         else if( !strcmp( str, "Image" ) )
97                 return PYMENU_IMAGE;
98         else if( !strcmp( str, "FaceSelect" ) )
99                 return PYMENU_FACESELECT;
100         else if( !strcmp( str, "WeightPaint" ) )
101                 return PYMENU_WEIGHTPAINT;
102         else if( !strcmp( str, "VertexPaint" ) )
103                 return PYMENU_VERTEXPAINT;
104         else if( !strcmp( str, "UVCalculation" ) )
105                 return PYMENU_UVCALCULATION;
106         else if( !strcmp( str, "Armature" ) )
107                 return PYMENU_ARMATURE;
108         else if( !strcmp( str, "ScriptTemplate" ) )
109                 return PYMENU_SCRIPTTEMPLATE;
110         else if( !strcmp( str, "TextPlugin" ) )
111                 return PYMENU_TEXTPLUGIN;
112         else if( !strcmp( str, "MeshFaceKey" ) )
113                 return PYMENU_MESHFACEKEY;
114         else if( !strcmp( str, "AddMesh" ) )
115                 return PYMENU_ADDMESH;
116         /* "Misc" or an inexistent group name: use misc */
117         else
118                 return PYMENU_MISC;
119 }
120
121 char *BPyMenu_group_itoa( short menugroup )
122 {
123         switch ( menugroup ) {
124         case PYMENU_EXPORT:
125                 return "Export";
126                 break;
127         case PYMENU_IMPORT:
128                 return "Import";
129                 break;
130         case PYMENU_ADD:
131                 return "Add";
132                 break;
133         case PYMENU_HELP:
134                 return "Help";
135                 break;
136         case PYMENU_HELPWEBSITES:
137                 return "HelpWebsites";
138                 break;
139         case PYMENU_HELPSYSTEM:
140                 return "HelpSystem";
141                 break;
142         case PYMENU_RENDER:
143                 return "Render";
144                 break;
145         case PYMENU_SYSTEM:
146                 return "System";
147                 break;
148         case PYMENU_OBJECT:
149                 return "Object";
150                 break;
151         case PYMENU_MESH:
152                 return "Mesh";
153                 break;
154         case PYMENU_THEMES:
155                 return "Themes";
156                 break;
157         case PYMENU_WIZARDS:
158                 return "Wizards";
159                 break;
160         case PYMENU_ANIMATION:
161                 return "Animation";
162                 break;
163         case PYMENU_MATERIALS:
164                 return "Materials";
165                 break;
166         case PYMENU_UV:
167                 return "UV";
168                 break;
169         case PYMENU_IMAGE:
170                 return "Image";
171                 break;
172         case PYMENU_FACESELECT:
173                 return "FaceSelect";
174                 break;
175         case PYMENU_WEIGHTPAINT:
176                 return "WeightPaint";
177                 break;
178         case PYMENU_VERTEXPAINT:
179                 return "VertexPaint";
180                 break;
181         case PYMENU_UVCALCULATION:
182                 return "UVCalculation";
183                 break;
184         case PYMENU_ARMATURE:
185                 return "Armature";
186                 break;
187         case PYMENU_SCRIPTTEMPLATE:
188                 return "ScriptTemplate";
189                 break;
190         case PYMENU_TEXTPLUGIN:
191                 return "TextPlugin";
192                 break;
193         case PYMENU_MESHFACEKEY:
194                 return "MeshFaceKey";
195                 break;
196         case PYMENU_ADDMESH:
197                 return "AddMesh";
198                 break;
199         case PYMENU_MISC:
200                 return "Misc";
201                 break;
202         }
203         return NULL;
204 }
205
206 /* BPyMenu_CreatePupmenuStr:
207  * build and return a meaninful string to be used by pupmenu().  The
208  * string is made of a bpymenu name as title and its submenus as possible
209  * choices for the user.
210 */
211 char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short menugroup )
212 {
213         BPySubMenu *pysm = pym->submenus;
214         char str[1024], str2[100];
215         int i = 0, rlen;
216
217         if( !pym || !pysm )
218                 return NULL;
219
220         str[0] = '\0';
221
222         PyOS_snprintf( str2, sizeof( str2 ), "%s: %s%%t",
223                        BPyMenu_group_itoa( menugroup ), pym->name );
224         strcat( str, str2 );
225
226         while( pysm ) {
227                 PyOS_snprintf( str2, sizeof( str2 ), "|%s%%x%d", pysm->name,
228                                i );
229                 rlen = sizeof( str ) - strlen( str );
230                 strncat( str, str2, rlen );
231                 i++;
232                 pysm = pysm->next;
233         }
234
235         return BLI_strdup( str );
236 }
237
238 static void bpymenu_RemoveAllSubEntries( BPySubMenu * smenu )
239 {
240         BPySubMenu *tmp;
241
242         while( smenu ) {
243                 tmp = smenu->next;
244                 if( smenu->name )
245                         MEM_freeN( smenu->name );
246                 if( smenu->arg )
247                         MEM_freeN( smenu->arg );
248                 MEM_freeN( smenu );
249                 smenu = tmp;
250         }
251         return;
252 }
253
254 void BPyMenu_RemoveAllEntries( void )
255 {
256         BPyMenu *tmp, *pymenu;
257         int i;
258
259         for( i = 0; i < PYMENU_TOTAL; i++ ) {
260                 pymenu = BPyMenuTable[i];
261                 while( pymenu ) {
262                         tmp = pymenu->next;
263                         if( pymenu->name )
264                                 MEM_freeN( pymenu->name );
265                         if( pymenu->filename )
266                                 MEM_freeN( pymenu->filename );
267                         if( pymenu->tooltip )
268                                 MEM_freeN( pymenu->tooltip );
269                         if( pymenu->submenus )
270                                 bpymenu_RemoveAllSubEntries( pymenu->
271                                                              submenus );
272                         MEM_freeN( pymenu );
273                         pymenu = tmp;
274                 }
275                 BPyMenuTable[i] = NULL;
276         }
277
278         Dirs_Number = 0;
279         Dir_Depth = 0;
280
281         return;
282 }
283
284 static BPyMenu *bpymenu_FindEntry( short group, char *name )
285 {
286         BPyMenu *pymenu;
287
288         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
289                 return NULL;
290
291         pymenu = BPyMenuTable[group];
292
293         while( pymenu ) {
294                 if( !strcmp( pymenu->name, name ) )
295                         return pymenu;
296                 pymenu = pymenu->next;
297         }
298
299         return NULL;
300 }
301
302 /* BPyMenu_GetEntry:
303  * given a group and a position, return the entry in that position from
304  * that group.
305 */
306 BPyMenu *BPyMenu_GetEntry( short group, short pos )
307 {
308         BPyMenu *pym = NULL;
309
310         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
311                 return NULL;
312
313         pym = BPyMenuTable[group];
314
315         while( pos-- ) {
316                 if( pym )
317                         pym = pym->next;
318                 else
319                         break;
320         }
321
322         return pym;             /* found entry or NULL */
323 }
324
325 static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip )
326 {
327         if( !pymenu )
328                 return;
329
330         if( pymenu->tooltip )
331                 MEM_freeN( pymenu->tooltip );
332         pymenu->tooltip = BLI_strdup( tip );
333
334         return;
335 }
336
337 static void bpymenu_set_shortcut( BPyMenu * pymenu, char *combi )
338 {
339         unsigned short key, qual;
340
341         if( !pymenu )
342                 return;
343
344         if (!decode_key_string(combi, &key, &qual)) {
345                 return; /* TODO: Print some error */
346         }
347
348         pymenu->key = key;
349         pymenu->qual = qual;
350
351         return;
352 }
353
354 /* bpymenu_AddEntry:
355  * try to find an existing pymenu entry with the given type and name;
356  * if found, update it with new info, otherwise create a new one and fill it.
357  */
358 static BPyMenu *bpymenu_AddEntry( short group, short version, char *name,
359         char *fname, int is_userdir, char *tooltip )
360 {
361         BPyMenu *menu, *next = NULL, **iter;
362         int nameclash = 0;
363
364         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
365                 return NULL;
366         if( !name || !fname )
367                 return NULL;
368
369         menu = bpymenu_FindEntry( group, name );        /* already exists? */
370
371         /* if a menu with this name already exists in the same group:
372          * - if one script is in the default dir and the other in U.pythondir,
373          *   accept and let the new one override the other.
374          * - otherwise, report the error and return NULL. */
375         if( menu ) {
376                 if( menu->dir < is_userdir ) {  /* new one is in U.pythondir */
377                         nameclash = 1;
378                         if( menu->name )
379                                 MEM_freeN( menu->name );
380                         if( menu->filename )
381                                 MEM_freeN( menu->filename );
382                         if( menu->tooltip )
383                                 MEM_freeN( menu->tooltip );
384                         if( menu->submenus )
385                                 bpymenu_RemoveAllSubEntries( menu->submenus );
386                         next = menu->next;
387                 } else {        /* they are in the same dir */
388                         if (DEBUG) {
389                                 fprintf(stderr, "\n\
390 Warning: script %s's menu name is already in use.\n\
391 Edit the script and change its \n\
392 Name: '%s'\n\
393 field, please.\n\
394 Note: if you really want to have two scripts for the same menu with\n\
395 the same name, keep one in the default dir and the other in\n\
396 the user defined dir (only the later will be registered).\n", fname, name);
397                         }
398                         return NULL;
399                 }
400         } else
401                 menu = MEM_mallocN( sizeof( BPyMenu ), "pymenu" );
402
403         if( !menu )
404                 return NULL;
405
406         menu->name = BLI_strdup( name );
407         menu->version = version;
408         menu->filename = BLI_strdup( fname );
409         menu->tooltip = NULL;
410         if( tooltip )
411                 menu->tooltip = BLI_strdup( tooltip );
412         menu->dir = is_userdir;
413         menu->submenus = NULL;
414         menu->next = next;      /* non-NULL if menu already existed */
415
416         if( nameclash )
417                 return menu;    /* no need to place it, it's already at the list */
418         else {  /* insert the new entry in its correct position at the table */
419                 BPyMenu *prev = NULL;
420                 char *s = NULL;
421
422                 iter = &BPyMenuTable[group];
423                 while( *iter ) {
424                         s = ( *iter )->name;
425                         if( s )
426                                 if( strcmp( menu->name, s ) < 0 )
427                                         break;  /* sort by names */
428                         prev = *iter;
429                         iter = &( ( *iter )->next );
430                 }
431
432                 if( *iter ) {   /* prepend */
433                         menu->next = *iter;
434                         if( prev )
435                                 prev->next = menu;
436                         else
437                                 BPyMenuTable[group] = menu;     /* is first entry */
438                 } else
439                         *iter = menu;   /* append */
440         }
441
442         return menu;
443 }
444
445 /* bpymenu_AddSubEntry:
446  * add a submenu to an existing python menu.
447  */
448 static int bpymenu_AddSubEntry( BPyMenu * mentry, char *name, char *arg )
449 {
450         BPySubMenu *smenu, **iter;
451
452         smenu = MEM_mallocN( sizeof( BPySubMenu ), "pysubmenu" );
453         if( !smenu )
454                 return -1;
455
456         smenu->name = BLI_strdup( name );
457         smenu->arg = BLI_strdup( arg );
458         smenu->next = NULL;
459
460         if( !smenu->name || !smenu->arg )
461                 return -1;
462
463         iter = &( mentry->submenus );
464         while( *iter )
465                 iter = &( ( *iter )->next );
466
467         *iter = smenu;
468
469         return 0;
470 }
471
472 /* bpymenu_CreateFromFile:
473  * parse the bpymenus data file where Python menu data is stored;
474  * based on this data, create and fill the pymenu structs.
475  */
476 static int bpymenu_CreateFromFile( void )
477 {
478         FILE *fp;
479         char line[255], w1[255], w2[255], tooltip[255], *tip;
480         char upythondir[FILE_MAX];
481         char *homedir = NULL;
482         int parsing, version, is_userdir;
483         short group;
484         BPyMenu *pymenu = NULL;
485
486         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
487          * for each available cathegory: import, export, etc.) */
488         for( group = 0; group < PYMENU_TOTAL; group++ )
489                 BPyMenuTable[group] = NULL;
490
491         /* let's try to open the file with bpymenu data */
492         homedir = bpy_gethome(0);
493         if (!homedir) {
494                 if( DEBUG )
495                         fprintf(stderr,
496                                 "BPyMenus error: couldn't open config file Bpymenus: no home dir.\n");
497                 return -1;
498         }
499
500         BLI_make_file_string( "/", line, homedir, BPYMENU_DATAFILE );
501
502         fp = fopen( line, "rb" );
503
504         if( !fp ) {
505                 if( DEBUG )
506                         fprintf(stderr, "BPyMenus error: couldn't open config file %s.\n", line );
507                 return -1;
508         }
509
510         fgets( line, 255, fp ); /* header */
511
512         /* check if the U.pythondir we saved at the file is different from the
513          * current one.  If so, return to force updating from dirs */
514         w1[0] = '\0';
515         fscanf( fp, "# User defined scripts dir: %[^\n]\n", w1 );
516
517                 BLI_strncpy(upythondir, U.pythondir, FILE_MAX);
518                 BLI_convertstringcode(upythondir, G.sce);
519
520                 if( strcmp( w1, upythondir ) != 0 )
521                         return -1;
522
523                 w1[0] = '\0';
524
525         while( fgets( line, 255, fp ) ) {       /* parsing file lines */
526
527                 switch ( line[0] ) {    /* check first char */
528                 case '#':       /* comment */
529                         continue;
530                         break;
531                 case '\n':
532                         continue;
533                         break;
534                 default:
535                         parsing = sscanf( line, "%s {\n", w1 ); /* menu group */
536                         break;
537                 }
538
539                 if( parsing == 1 ) {    /* got menu group string */
540                         group = (short)bpymenu_group_atoi( w1 );
541                         if( group < 0 && DEBUG ) {      /* invalid type */
542                                 fprintf(stderr,
543                                         "BPyMenus error parsing config file: wrong group: %s,\n\
544 will use 'Misc'.\n", w1 );
545                         }
546                 } else
547                         continue;
548
549                 for(;;) {
550                         tip = NULL;     /* optional tooltip */
551                         fgets( line, 255, fp );
552                         if( line[0] == '}' )
553                                 break;
554                         else if( line[0] == '\n' )
555                                 continue;
556                         else if( line[0] == '\'' ) {    /* menu entry */
557                                 parsing =
558                                         sscanf( line,
559                                                 "'%[^']' %d %s %d '%[^']'\n",
560                                                 w1, &version, w2, &is_userdir,
561                                                 tooltip );
562
563                                 if( parsing <= 0 ) {    /* invalid line, get rid of it */
564                                         fgets( line, 255, fp );
565                                 } else if( parsing == 5 )
566                                         tip = tooltip;  /* has tooltip */
567
568                                 pymenu = bpymenu_AddEntry( group,
569                                                            ( short ) version,
570                                                            w1, w2, is_userdir,
571                                                            tip );
572                                 if( !pymenu ) {
573                                         puts( "BPyMenus error: couldn't create bpymenu entry.\n" );
574                                         fclose( fp );
575                                         return -1;
576                                 }
577                         } else if( line[0] == '|' && line[1] == '_' ) { /* menu sub-entry */
578                                 if( !pymenu )
579                                         continue;       /* no menu yet, skip this line */
580                                 sscanf( line, "|_%[^:]: %s\n", w1, w2 );
581                                 bpymenu_AddSubEntry( pymenu, w1, w2 );
582                         }
583                 }
584         }
585
586         fclose( fp );
587         return 0;
588 }
589
590 /* bpymenu_WriteDataFile:
591  * writes the registered scripts info to the user's home dir, for faster
592  * access when the scripts dir hasn't changed.
593 */
594 static void bpymenu_WriteDataFile( void )
595 {
596         BPyMenu *pymenu;
597         BPySubMenu *smenu;
598         FILE *fp;
599         char fname[FILE_MAXDIR], *homedir;
600         int i;
601
602         homedir = bpy_gethome(0);
603
604         if (!homedir) {
605                 if( DEBUG )
606                         fprintf(stderr,
607                                 "BPyMenus error: couldn't write Bpymenus file: no home dir.\n\n");
608                 return;
609         }
610
611         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
612
613         fp = fopen( fname, "w" );
614         if( !fp ) {
615                 if( DEBUG )
616                         fprintf(stderr, "BPyMenus error: couldn't write %s file.\n\n",
617                                 fname );
618                 return;
619         }
620
621         fprintf( fp,
622                  "# Blender: registered menu entries for bpython scripts\n" );
623
624         if (U.pythondir[0] != '\0' &&
625                         strcmp(U.pythondir, "/") != 0 && strcmp(U.pythondir, "//") != 0)
626         {
627                 char upythondir[FILE_MAX];
628
629                 BLI_strncpy(upythondir, U.pythondir, FILE_MAX);
630                 BLI_convertstringcode(upythondir, G.sce);
631                 fprintf( fp, "# User defined scripts dir: %s\n", upythondir );
632         }
633
634         for( i = 0; i < PYMENU_TOTAL; i++ ) {
635                 pymenu = BPyMenuTable[i];
636                 if( !pymenu )
637                         continue;
638                 fprintf( fp, "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
639                 while( pymenu ) {
640                         fprintf( fp, "'%s' %d %s %d", pymenu->name,
641                                  pymenu->version, pymenu->filename,
642                                  pymenu->dir );
643                         if( pymenu->tooltip )
644                                 fprintf( fp, " '%s'\n", pymenu->tooltip );
645                         else
646                                 fprintf( fp, "\n" );
647                         smenu = pymenu->submenus;
648                         while( smenu ) {
649                                 fprintf( fp, "|_%s: %s\n", smenu->name,
650                                          smenu->arg );
651                                 smenu = smenu->next;
652                         }
653                         pymenu = pymenu->next;
654                 }
655                 fprintf( fp, "}\n" );
656         }
657
658         fclose( fp );
659         return;
660 }
661
662 /* BPyMenu_PrintAllEntries:
663  * useful for debugging.
664  */
665 void BPyMenu_PrintAllEntries( void )
666 {
667         BPyMenu *pymenu;
668         BPySubMenu *smenu;
669         int i;
670
671         printf( "# Blender: registered menu entries for bpython scripts\n" );
672
673         for( i = 0; i < PYMENU_TOTAL; i++ ) {
674                 pymenu = BPyMenuTable[i];
675                 printf( "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
676                 while( pymenu ) {
677                         printf( "'%s' %d %s %d", pymenu->name, pymenu->version,
678                                 pymenu->filename, pymenu->dir );
679                         if( pymenu->tooltip )
680                                 printf( " '%s'\n", pymenu->tooltip );
681                         else
682                                 printf( "\n" );
683                         smenu = pymenu->submenus;
684                         while( smenu ) {
685                                 printf( "|_%s: %s\n", smenu->name,
686                                         smenu->arg );
687                                 smenu = smenu->next;
688                         }
689                         pymenu = pymenu->next;
690                 }
691                 printf( "}\n" );
692         }
693 }
694
695 /* bpymenu_ParseFile:
696  * recursively scans folders looking for scripts to register.
697  *
698  * This function scans the scripts directory looking for .py files with the
699  * right header and menu info, using that to fill the bpymenu structs.
700  * is_userdir defines if the script is in the default scripts dir or the
701  * user defined one (U.pythondir: is_userdir == 1).
702  * Speed is important.
703  *
704  * The first line of the script must be '#!BPY'.
705  * The header registration lines must appear between the first pair of
706  * '\"\"\"' and follow this order (the single-quotes are part of
707  * the format):
708  *
709  * # \"\"\"<br>
710  * # Name: 'script name for the menu'
711  * # Blender: <code>short int</code> (minimal Blender version)
712  * # Group: 'group name' (defines menu)
713  * # Submenu: 'submenu name' related_1word_arg
714  * # Shortcut: Modifier+Key (optional shortcut combination for supported groups)
715  * # Tooltip: 'tooltip for the menu'
716  * # \"\"\"
717  *
718  * Notes:
719  *
720  * - Commenting out header lines with "#" is optional, but recommended.
721  * - There may be more than one submenu line, or none:
722  *   submenus and the tooltip are optional;
723  * - The Blender version is the same number reported by 
724  *   Blender.Get('version') in BPython or G.version in C;
725  * - Line length must be less than 99.
726  */
727 static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir)
728 {
729         char line[100];
730         char head[100];
731         char middle[100];
732         char tail[100];
733         int matches;
734         int parser_state;
735
736         char script_name[100];
737         int script_version = 1;
738         int script_group;
739
740         BPyMenu *scriptMenu = NULL;
741
742         if (file != NULL) {
743                 parser_state = 1; /* state of parser, 0 to terminate */
744
745                 while ((parser_state != 0) && (fgets(line, 100, file) != NULL)) {
746
747                         switch (parser_state) {
748
749                                 case 1: /* !BPY */
750                                         if (strncmp(line, "#!BPY", 5) == 0) {
751                                                 parser_state++;
752                                         } else {
753                                                 parser_state = 0;
754                                         }
755                                         break;
756
757                                 case 2: /* \"\"\" */
758                                         if ((strstr(line, "\"\"\""))) {
759                                                 parser_state++;
760                                         }
761                                         break;
762
763                                 case 3: /* Name: 'script name for the menu' */
764                                         matches = sscanf(line, "%[^']'%[^']'%c", head, script_name, tail);
765                                         if ((matches == 3) && (strstr(head, "Name:") != NULL)) {
766                                                 parser_state++;
767                                         } else {
768                                                 if (DEBUG)
769                                                         fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fname);
770                                                 parser_state = 0;
771                                         }
772                                         break;
773
774                                 case 4: /* Blender: <short int> */
775                                         matches = sscanf(line, "%[^1234567890]%i%c", head, &script_version,
776                                                 tail);
777                                         if (matches == 3) {
778                                                 parser_state++;
779                                         } else {
780                                                 if (DEBUG)
781                                                         fprintf(stderr,"BPyMenus error: Wrong 'Blender' line: %s\n",fname);
782                                                 parser_state = 0;
783                                         }
784                                         break;
785
786                                 case 5: /* Group: 'group name' */
787                                         matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
788                                         if ((matches == 3) && (strstr(head, "Group:") != NULL)) {
789                                                 script_group = bpymenu_group_atoi(middle);
790                                                 if (script_group < 0) {
791                                                         if (DEBUG)
792                                                                 fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n",
793                                                                         middle, fname);
794                                                         parser_state = 0;
795                                                 }
796                                                 
797                                                 else { /* register script */
798                                                         scriptMenu = bpymenu_AddEntry((short)script_group,
799                                                                 (short int)script_version, script_name, fname, is_userdir,NULL);
800                                                         if (scriptMenu == NULL) {
801                                                                 if (DEBUG)
802                                                                         fprintf(stderr,
803                                                                                 "BPyMenus error: Couldn't create entry for: %s\n", fname);
804                                                                 parser_state = 0;
805                                                         } else {
806                                                                 parser_state++;
807                                                         }
808                                                 }
809
810                                         } else {
811                                                 if (DEBUG)
812                                                         fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n",fname);
813                                                 parser_state = 0;
814                                         }
815                                         break;
816
817                                 case 6: /* optional elements */
818                                         /* Submenu: 'submenu name' related_1word_arg */
819                                         matches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail);
820                                         if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) {
821                                                 bpymenu_AddSubEntry(scriptMenu, middle, tail);
822                                         } else {
823                                                 /* Shortcut: 'key+combination' */
824                                                 matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
825                                                 if ((matches == 3) && (strstr(head, "Shortcut:") != NULL)) {
826                                                         bpymenu_set_shortcut(scriptMenu, middle);
827                                                 } else {
828                                                         /* Tooltip: 'tooltip for the menu */
829                                                         matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
830                                                         if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) ||
831                                                                 (strstr(head, "Tip:") != NULL))) {
832                                                                 bpymenu_set_tooltip(scriptMenu, middle);
833                                                         }
834                                                         parser_state = 0;
835                                                 }
836                                         }
837                                         break;
838
839                                 default:
840                                         parser_state = 0;
841                                         break;
842                         }
843                 }
844         }
845
846         else { /* shouldn't happen, it's checked in bpymenus_ParseDir */
847                 if (DEBUG)
848                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", fname);
849                 return -1;
850         }
851
852         return 0;
853 }
854
855 /* bpymenu_ParseDir:
856  * recursively scans folders looking for scripts to register.
857  *
858  * This function scans the scripts directory looking for .py files with the
859  * right header and menu info.
860  * - is_userdir defines if the script is in the default scripts dir or the
861  * user defined one (U.pythondir: is_userdir == 1);
862  * - parentdir is the parent dir name to store as part of the script filename,
863  * if we're down a subdir.
864  * Speed is important.
865  */
866 static int bpymenu_ParseDir(char *dirname, char *parentdir, int is_userdir )
867 {
868         DIR *dir; 
869         FILE *file = NULL;
870         struct dirent *de;
871         struct stat status;
872         char *file_extension;
873         char path[FILE_MAX];
874         char subdir[FILE_MAX];
875         char *s = NULL;
876         
877         dir = opendir(dirname);
878
879         if (dir != NULL) {
880                 while ((de = readdir(dir)) != NULL) {
881
882                         /* skip files and dirs starting with '.' or 'bpy' */
883                         if ((de->d_name[0] == '.') || !strncmp(de->d_name, "bpy", 3)) {
884                                 continue;
885                         }
886                         
887                         BLI_make_file_string("/", path, dirname, de->d_name);
888                         
889                         if (stat(path, &status) != 0) {
890                                 if (DEBUG)
891                                         fprintf(stderr, "stat %s failed: %s\n", path, strerror(errno));
892                         }
893
894                         if (S_ISREG(status.st_mode)) { /* is file */
895
896                                 file_extension = strstr(de->d_name, ".py");
897
898                                 if (file_extension && *(file_extension + 3) == '\0') {
899                                         file = fopen(path, "rb");
900
901                                         if (file) {
902                                                 s = de->d_name;
903                                                 if (parentdir) {
904                                                         /* Join parentdir and de->d_name */
905                                                         BLI_join_dirfile(subdir, parentdir, de->d_name);
906
907                                                         s = subdir;
908                                                 }
909                                                 bpymenu_ParseFile(file, s, is_userdir);
910                                                 fclose(file);
911                                         }
912
913                                         else {
914                                                 if (DEBUG)
915                                                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", path);
916                                         }
917                                 }
918                         }
919
920                         else if (S_ISDIR(status.st_mode)) { /* is subdir */
921                                 Dirs_Number++;
922                                 Dir_Depth++;
923                                 if (Dirs_Number > MAX_DIR_NUMBER) {
924                                         if (DEBUG) {
925                                                 fprintf(stderr, "BPyMenus error: too many subdirs.\n");
926                                         }
927                                         closedir(dir);
928                                         return -1;
929                                 }
930                                 else if (Dir_Depth > MAX_DIR_DEPTH) {
931                                         if (DEBUG)
932                                                 fprintf(stderr,
933                                                         "BPyMenus error: max depth reached traversing dir tree.\n");
934                                         closedir(dir);
935                                         return -1;
936                                 }
937                                 s = de->d_name;
938                                 if (parentdir) {
939                                         /* Join parentdir and de->d_name */
940                                         BLI_join_dirfile(subdir, parentdir, de->d_name);                                        
941                                         s = subdir;
942                                 }
943                                 if (bpymenu_ParseDir(path, s, is_userdir) == -1) {
944                                         closedir(dir);
945                                         return -1;
946                                 }
947                                 Dir_Depth--;
948                         }
949
950                 }
951                 closedir(dir);
952         }
953
954         else { /* open directory stream failed */
955                 if (DEBUG)
956                         fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno));
957                 return -1;
958         }
959
960         return 0;
961 }
962
963 static int bpymenu_GetStatMTime( const char *name, int is_file, time_t * mtime )
964 {
965         struct stat st;
966         int result;
967
968 #ifdef WIN32
969         if (is_file) {
970         result = stat( name, &st );
971         } else {
972                 /* needed for win32 only, remove trailing slash */
973                 char name_stat[FILE_MAX];
974                 BLI_strncpy(name_stat, name, FILE_MAX);
975                 BLI_del_slash(name_stat);
976                 result = stat( name_stat, &st );
977         }
978 #else
979         result = stat( name, &st );
980 #endif
981         
982         if( result == -1 )
983                 return -1;
984
985         if( is_file ) {
986                 if( !S_ISREG( st.st_mode ) )
987                         return -2;
988         } else if( !S_ISDIR( st.st_mode ) )
989                 return -2;
990
991         *mtime = st.st_mtime;
992
993         return 0;
994 }
995
996 /* BPyMenu_Init:
997  * import the bpython menus data to Blender, either from:
998  * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or
999  * - the scripts dir(s), case newer than the datafile (then update the file).
1000  * then fill the bpymenu table with this data.
1001  * if param usedir != 0, then the data is recreated from the dir(s) anyway.
1002 */
1003 int BPyMenu_Init( int usedir )
1004 {
1005         char fname[FILE_MAXDIR];
1006         char dirname[FILE_MAX];
1007         char upythondir[FILE_MAX];
1008         char *upydir = U.pythondir, *sdir = NULL;
1009         time_t time_dir1 = 0, time_dir2 = 0, time_file = 0;
1010         int stat_dir1 = 0, stat_dir2 = 0, stat_file = 0;
1011         int i;
1012
1013         DEBUG = G.f & G_DEBUG;  /* is Blender in debug mode (started with -d) ? */
1014
1015         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
1016          * for each available group: import, export, etc.) */
1017         for( i = 0; i < PYMENU_TOTAL; i++ )
1018                 BPyMenuTable[i] = NULL;
1019
1020         if( DEBUG )
1021                 fprintf(stdout, "\nRegistering scripts in Blender menus ...\n\n" );
1022
1023         if( U.pythondir[0] == '\0') {
1024                 upydir = NULL;
1025         }
1026         else if (strcmp(U.pythondir, "/") == 0 || strcmp(U.pythondir, "//") == 0) {
1027                 /* these are not accepted to prevent possible slight slowdowns on startup;
1028                  * they should not be used as user defined scripts dir, anyway, also from
1029                  * speed considerations, since they'd not be dedicated scripts dirs */
1030                 if (DEBUG) fprintf(stderr,
1031                         "BPyMenus: invalid user defined Python scripts dir: \"/\" or \"//\".\n");
1032                 upydir = NULL;
1033         }
1034         else {
1035                 BLI_strncpy(upythondir, upydir, FILE_MAX);
1036                 BLI_convertstringcode(upythondir, G.sce);
1037         }
1038
1039         sdir = bpy_gethome(1);
1040
1041         if (sdir) {
1042                 BLI_strncpy(dirname, sdir, FILE_MAX);
1043                 stat_dir1 = bpymenu_GetStatMTime( dirname, 0, &time_dir1 );
1044
1045                 if( stat_dir1 < 0 ) {
1046                         time_dir1 = 0;
1047                         if( DEBUG ) {
1048                                 fprintf(stderr,
1049                                         "\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno));
1050                                 if( upydir )
1051                                         fprintf(stdout,
1052                                                 "Getting scripts menu data from user defined dir: %s.\n",
1053                                                 upythondir );
1054                         }
1055                 }
1056         }
1057         else stat_dir1 = -1;
1058
1059         if( upydir ) {
1060                 stat_dir2 = bpymenu_GetStatMTime( upythondir, 0, &time_dir2 );
1061
1062                 if( stat_dir2 < 0 ) {
1063                         time_dir2 = 0;
1064                         upydir = NULL;
1065                         if( DEBUG )
1066                                 fprintf(stderr, "\nUser defined scripts dir: %s:\n%s.\n",
1067                                         upythondir, strerror( errno ) );
1068                         if( stat_dir1 < 0 ) {
1069                                 if( DEBUG )
1070                                         fprintf(stderr, "\
1071 To have scripts in menus, please add them to the default scripts dir:\n\
1072 %s\n\
1073 and / or go to 'Info window -> File Paths tab' and set a valid path for\n\
1074 the user defined Python scripts dir.\n", dirname );
1075                                 return -1;
1076                         }
1077                 }
1078         }
1079         else stat_dir2 = -1;
1080
1081         if( ( stat_dir1 < 0 ) && ( stat_dir2 < 0 ) ) {
1082                 if( DEBUG ) {
1083                         fprintf(stderr, "\nCannot register scripts in menus, no scripts dir"
1084                                                         " available.\nExpected default dir at: %s \n", dirname );
1085                 }
1086                 return -1;
1087         }
1088
1089         if (usedir) stat_file = -1;
1090         else { /* if we're not forced to use the dir */
1091                 char *homedir = bpy_gethome(0);
1092
1093                 if (homedir) {
1094                         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
1095                         stat_file = bpymenu_GetStatMTime( fname, 1, &time_file );
1096                         if( stat_file < 0 )
1097                                 time_file = 0;
1098
1099                 /* comparing dates */
1100
1101                         if((stat_file == 0)
1102                                 && (time_file > time_dir1) && (time_file > time_dir2))
1103                         {       /* file is newer */
1104                                 stat_file = bpymenu_CreateFromFile(  ); /* -1 if an error occurred */
1105                                 if( !stat_file && DEBUG )
1106                                         fprintf(stdout,
1107                                                 "Getting menu data for scripts from file:\n%s\n\n", fname );
1108                         }
1109                         else stat_file = -1;
1110                 }
1111                 else stat_file = -1;    /* -1 to use dirs: didn't use file or it was corrupted */
1112         }
1113
1114         if( stat_file == -1 ) { /* use dirs */
1115                 if( DEBUG ) {
1116                         fprintf(stdout,
1117                                 "Getting menu data for scripts from dir(s):\ndefault: %s\n", dirname );
1118                         if( upydir )
1119                                 fprintf(stdout, "user defined: %s\n", upythondir );
1120                         fprintf(stdout, "\n");
1121                 }
1122                 if( stat_dir1 == 0 ) {
1123                         i = bpymenu_ParseDir( dirname, NULL, 0 );
1124                         if (i == -1 && DEBUG)
1125                                 fprintf(stderr, "Default scripts dir does not seem valid.\n\n");
1126                 }
1127                 if( stat_dir2 == 0 ) {
1128                         BLI_strncpy(dirname, U.pythondir, FILE_MAX);
1129                         BLI_convertstringcode(dirname, G.sce);
1130                         i = bpymenu_ParseDir( dirname, NULL, 1 );
1131                         if (i == -1 && DEBUG)
1132                                 fprintf(stderr, "User defined scripts dir does not seem valid.\n\n");
1133                 }
1134
1135                 /* check if we got any data */
1136                 for( i = 0; i < PYMENU_TOTAL; i++ )
1137                         if( BPyMenuTable[i] )
1138                                 break;
1139
1140                 /* if we got, recreate the file */
1141                 if( i < PYMENU_TOTAL )
1142                         bpymenu_WriteDataFile(  );
1143                 else if( DEBUG ) {
1144                         fprintf(stderr, "\n\
1145 Warning: Registering scripts in menus -- no info found.\n\
1146 Either your scripts dirs have no .py scripts or the scripts\n\
1147 don't have a header with registration data.\n\
1148 Default scripts dir is:\n\
1149 %s\n", dirname );
1150                         if( upydir )
1151                                 fprintf(stderr, "User defined scripts dir is: %s\n",
1152                                         upythondir );
1153                 }
1154         }
1155
1156         return 0;
1157 }