Changeset 750 for soft/giet_vm/giet_fat32/fat32.c
- Timestamp:
- Jan 2, 2016, 4:40:33 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
soft/giet_vm/giet_fat32/fat32.c
r746 r750 45 45 #include <mmc_driver.h> 46 46 #include <tty0.h> 47 #include <stdio.h> 47 48 48 49 ////////////////////////////////////////////////////////////////////////////////// … … 112 113 unsigned int buf_vaddr, 113 114 unsigned int count ); 114 115 //////////////////////////////////////////////////////////////////////////////////116 // The following function returns in the "desc" argument a pointer on a buffer117 // descriptor contained in a File_Cache, or in the Fat_Cache.118 // The searched buffer is idenfified by the "inode" and "cluster_id" arguments.119 // If the "inode" pointer is not NULL, the searched cache is a File-Cache.120 // If the "inode" pointer is NULL, the searched cache is the Fat-Cache,121 // The "cluster_id" argument is the buffer index in the file (or in the FAT).122 // In case of miss, it allocate a 4 Kbytes buffer and a cluster descriptor123 // from the local kernel heap, and calls the _fat_ioc_access() function to load124 // the missing cluster from the block device.125 // It returns 0 on success.126 // It returns 1 on error.127 //////////////////////////////////////////////////////////////////////////////////128 129 static unsigned int _get_buffer_from_cache( fat_inode_t* inode,130 unsigned int cluster_id,131 fat_cache_desc_t** desc );132 115 133 116 //////////////////////////////////////////////////////////////////////////////// … … 963 946 964 947 965 //////////////////////////////////////////////////////////////////////966 static unsigned int _get_buffer_from_cache( fat_inode_t* inode,967 unsigned int cluster_id,968 fat_cache_desc_t** desc )969 {970 // get cache pointer and levels971 fat_cache_node_t* node; // pointer on a 64-tree node972 unsigned int level; // cache level973 974 if ( inode == NULL ) // searched cache is the Fat-Cache975 {976 node = _fat.fat_cache_root;977 level = _fat.fat_cache_levels;978 979 #if (GIET_DEBUG_FAT & 1)980 if ( _get_proctime() > GIET_DEBUG_FAT )981 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): enters in FAT-Cache"982 " for cluster_id = %d\n", cluster_id );983 #endif984 }985 else // searched cache is a File-Cache986 {987 // add cache levels if needed988 while ( _get_levels_from_size( (cluster_id + 1) * 4096 ) > inode->levels )989 {990 #if (GIET_DEBUG_FAT & 1)991 if ( _get_proctime() > GIET_DEBUG_FAT )992 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): adding a File-Cache level\n" );993 #endif994 995 inode->cache = _allocate_one_cache_node( inode->cache );996 inode->levels++;997 }998 999 node = inode->cache;1000 level = inode->levels;1001 1002 #if (GIET_DEBUG_FAT & 1)1003 if ( _get_proctime() > GIET_DEBUG_FAT )1004 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): enters in File-Cache <%s>"1005 " for cluster_id = %d\n", inode->name , cluster_id );1006 #endif1007 }1008 1009 // search the 64-tree cache from top to bottom1010 while ( level )1011 {1012 // compute child index at each level1013 unsigned int index = (cluster_id >> (6*(level-1))) & 0x3F;1014 1015 if ( level == 1 ) // last level => children are cluster descriptors1016 {1017 fat_cache_desc_t* pdesc = (fat_cache_desc_t*)node->children[index];1018 1019 if ( pdesc == NULL ) // miss1020 {1021 // get missing cluster index lba1022 unsigned int lba;1023 unsigned int next;1024 unsigned int current = inode->cluster;1025 unsigned int count = cluster_id;1026 1027 if ( inode == NULL ) // searched cache is the Fat-Cache1028 {1029 1030 #if (GIET_DEBUG_FAT & 1)1031 if ( _get_proctime() > GIET_DEBUG_FAT )1032 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): miss in FAT-Cache for cluster_id %d\n",1033 cluster_id );1034 #endif1035 lba = _fat.fat_lba + (cluster_id << 3);1036 }1037 else // searched cache is a File-Cache1038 {1039 1040 #if (GIET_DEBUG_FAT & 1)1041 if ( _get_proctime() > GIET_DEBUG_FAT )1042 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): miss in File-Cache <%s> "1043 "for cluster_id %d\n", inode->name, cluster_id );1044 #endif1045 while ( count )1046 {1047 if ( _get_fat_entry( current , &next ) ) return 1;1048 current = next;1049 count--;1050 }1051 lba = _cluster_to_lba( current );1052 }1053 1054 // allocate 4K buffer1055 void* buf = _malloc( 4096 );1056 1057 // load one cluster (8 blocks) from block device1058 if ( _fat_ioc_access( 1, // descheduling1059 1, // to memory1060 lba,1061 (unsigned int)buf,1062 8 ) )1063 {1064 _free( buf );1065 _printf("\n[FAT ERROR] _get_buffer_from_cache()"1066 ": cannot access block device for lba = %x\n", lba );1067 return 1;1068 }1069 1070 // allocate buffer descriptor1071 pdesc = _malloc( sizeof(fat_cache_desc_t) );1072 pdesc->lba = lba;1073 pdesc->buffer = buf;1074 pdesc->dirty = 0;1075 node->children[index] = pdesc;1076 1077 #if (GIET_DEBUG_FAT & 1)1078 if ( _get_proctime() > GIET_DEBUG_FAT )1079 _printf("\n[DEBUG FAT] _get_buffer_from_cache(): buffer loaded from device"1080 " at vaddr = %x\n", (unsigned int)buf );1081 #endif1082 }1083 1084 // return pdesc pointer1085 *desc = pdesc;1086 1087 // prepare next iteration1088 level--;1089 }1090 else // not last level => children are 64-tree nodes1091 {1092 fat_cache_node_t* child = (fat_cache_node_t*)node->children[index];1093 if ( child == NULL ) // miss1094 {1095 // allocate a cache node if miss1096 child = _allocate_one_cache_node( NULL );1097 node->children[index] = child;1098 }1099 1100 // prepare next iteration1101 node = child;1102 level--;1103 }1104 } // end while1105 1106 return 0;1107 } // end _get_buffer_from_cache()1108 1109 948 1110 949 … … 1170 1009 fat_cache_desc_t* pdesc; 1171 1010 unsigned int* buffer; 1172 if ( _ get_buffer_from_cache( NULL, // Fat-Cache1011 if ( _fat_buffer_from_cache( NULL, // Fat-Cache 1173 1012 cluster_id, 1174 1013 &pdesc ) ) return 1; … … 1195 1034 fat_cache_desc_t* pdesc; 1196 1035 unsigned int* buffer; 1197 if ( _ get_buffer_from_cache( NULL, // Fat-Cache1036 if ( _fat_buffer_from_cache( NULL, // Fat-Cache 1198 1037 cluster_id, 1199 1038 &pdesc ) ) return 1; … … 1679 1518 { 1680 1519 // get one 4 Kytes buffer from File_Cache 1681 if ( _ get_buffer_from_cache( inode,1520 if ( _fat_buffer_from_cache( inode, 1682 1521 cluster_id, 1683 1522 &pdesc ) ) return 1; … … 1761 1600 { 1762 1601 // get one 4 Kytes buffer from File_Cache 1763 if ( _ get_buffer_from_cache( parent,1602 if ( _fat_buffer_from_cache( parent, 1764 1603 cluster_id, 1765 1604 &pdesc ) ) return 1; … … 1819 1658 if ( offset >= 4096 ) // new buffer required 1820 1659 { 1821 if ( _ get_buffer_from_cache( parent,1660 if ( _fat_buffer_from_cache( parent, 1822 1661 cluster_id + 1, 1823 1662 &pdesc ) ) return 1; … … 1993 1832 fat_cache_desc_t* pdesc; 1994 1833 1995 if ( _ get_buffer_from_cache( inode->parent,1834 if ( _fat_buffer_from_cache( inode->parent, 1996 1835 cluster_id, 1997 1836 &pdesc ) ) return 1; … … 2010 1849 break; 2011 1850 2012 if ( _ get_buffer_from_cache( inode->parent,1851 if ( _fat_buffer_from_cache( inode->parent, 2013 1852 cluster_id - 1, 2014 1853 &pdesc ) ) return 1; … … 2046 1885 unsigned int offset = (inode->dentry & 0x7F)<<5; 2047 1886 2048 if ( _ get_buffer_from_cache( inode->parent,1887 if ( _fat_buffer_from_cache( inode->parent, 2049 1888 cluster_id, 2050 1889 &pdesc ) ) return 1; … … 2131 1970 // get one 4 Kytes buffer from parent File_Cache 2132 1971 fat_cache_desc_t* pdesc; 2133 if ( _ get_buffer_from_cache( parent,1972 if ( _fat_buffer_from_cache( parent, 2134 1973 cluster_id, 2135 1974 &pdesc ) ) return 2; … … 2148 1987 ord = _read_entry( LDIR_ORD , buffer + offset , 0 ); 2149 1988 2150 if (ord == NO_MORE_ENTRY) // no more entry in directory=> break1989 if (ord == NO_MORE_ENTRY) // no more entry => break 2151 1990 { 2152 1991 found = -1; … … 3187 3026 3188 3027 3189 ///////////////////////////////////////////////////////////////////////////////// 3190 // The following function implements the "giet_fat_read()" system call,3191 // but can also be used by the kernel in physical addressing mode.3028 /////////////////////////////////////////////////////////////////////////////////// 3029 // The following function implements the "giet_fat_read()" and "giet_fat_pread')" 3030 // system calls, but can also be used by the kernel in physical addressing mode. 3192 3031 // It transfers <count> bytes from the File_Cache associated to the file 3193 3032 // identified by <fd_id>, to the destination buffer defined by <vaddr>. … … 3196 3035 // the physical address is computed as extend[15:0] | vaddr[31:0] 3197 3036 // In case of miss in the File_Cache, it loads all involved clusters into cache. 3198 ///////////////////////////////////////////////////////////////////////////////// 3037 /////////////////////////////////////////////////////////////////////////////////// 3199 3038 // Returns the number of bytes actually transfered on success. 3200 3039 // Returns a negative value on error: … … 3207 3046 unsigned int vaddr, // destination buffer vaddr 3208 3047 unsigned int count, // number of bytes to read 3209 unsigned int extend ) // physical address extension 3048 unsigned int extend, // physical address extension 3049 unsigned int offset, // forced file offset 3050 unsigned int modes ) // special modes 3210 3051 { 3211 3052 … … 3217 3058 if ( _get_proctime() > GIET_DEBUG_FAT ) 3218 3059 _printf("\n[DEBUG FAT] _fat_read(): P[%d,%d,%d] enters at cycle %d\n" 3219 " fd = %d / vaddr = %x / bytes = %x / extend = %x \n",3060 " fd = %d / vaddr = %x / bytes = %x / extend = %x / forced_offset = %x\n", 3220 3061 x , y , p , _get_proctime(), 3221 fd_id , vaddr , count , extend );3062 fd_id , vaddr , count , extend , offset ); 3222 3063 #endif 3223 3064 … … 3225 3066 if( _fat.initialized != FAT_INITIALIZED ) 3226 3067 { 3227 _printf("\n[FAT ERROR] _fat_read(): FAT not initialized\n");3068 _printf("\n[FAT ERROR] in _fat_read(): FAT not initialized\n"); 3228 3069 return GIET_FAT32_NOT_INITIALIZED; 3229 3070 } … … 3232 3073 if ( fd_id >= GIET_OPEN_FILES_MAX ) 3233 3074 { 3234 _printf("\n[FAT ERROR] _fat_read(): illegal file descriptor\n");3075 _printf("\n[FAT ERROR] in _fat_read(): illegal file descriptor\n"); 3235 3076 return GIET_FAT32_INVALID_FD; 3236 3077 } 3237 3078 3238 // check file isopen3079 // check file open 3239 3080 if ( _fat.fd[fd_id].allocated == 0 ) 3240 3081 { 3241 _printf("\n[FAT ERROR] _fat_read(): file not open\n");3082 _printf("\n[FAT ERROR] in _fat_read(): file not open\n"); 3242 3083 return GIET_FAT32_NOT_OPEN; 3243 3084 } … … 3249 3090 _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 3250 3091 3092 // get special modes 3093 unsigned int physical_addressing = modes & FAT_PADDR_MODE; 3094 unsigned int forced_offset = modes & FAT_FORCED_OFFSET; 3095 3251 3096 // get file inode pointer and offset 3252 3097 fat_inode_t* inode = _fat.fd[fd_id].inode; 3253 unsigned int seek = _fat.fd[fd_id].seek;3098 unsigned int seek = forced_offset ? offset : _fat.fd[fd_id].seek; 3254 3099 3255 3100 // check seek versus file size … … 3259 3104 _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 3260 3105 3261 _printf("\n[FAT ERROR] _fat_read(): seeklarger than file size"3106 _printf("\n[FAT ERROR] in _fat_read(): offset larger than file size" 3262 3107 " / seek = %x / file_size = %x\n", 3263 3108 seek , inode->size ); … … 3293 3138 unsigned char* cbuf; 3294 3139 fat_cache_desc_t* pdesc; 3295 if ( _ get_buffer_from_cache( inode,3140 if ( _fat_buffer_from_cache( inode, 3296 3141 cluster_id, 3297 3142 &pdesc ) ) … … 3300 3145 _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 3301 3146 3302 _printf("\n[FAT ERROR] _fat_read(): cannot load file <%s>\n",3147 _printf("\n[FAT ERROR] in _fat_read(): cannot load file <%s>\n", 3303 3148 inode->name ); 3304 3149 return GIET_FAT32_IO_ERROR; … … 3338 3183 3339 3184 // move data 3340 if ( (extend & 0xFFFF0000) == 0 )// no physical addressing3185 if ( physical_addressing == 0 ) // no physical addressing 3341 3186 { 3342 3187 char* dest = (char*)(vaddr + done); … … 3346 3191 { 3347 3192 unsigned int flags; 3348 paddr_t pdest = (((paddr_t) (extend & 0x0000FFFF))<<32) + vaddr + done;3193 paddr_t pdest = (((paddr_t)extend)<<32) + vaddr + done; 3349 3194 paddr_t psource = _v2p_translate( (unsigned int)source, &flags ); 3350 3195 _physical_memcpy( pdest , psource , nbytes ); … … 3360 3205 #endif 3361 3206 3362 // update seek 3363 _fat.fd[fd_id].seek += done;3207 // update seek if required 3208 if ( forced_offset == 0 ) _fat.fd[fd_id].seek += done; 3364 3209 3365 3210 // release lock … … 3379 3224 // identified by <fd_id>, from the source buffer defined by <vaddr>. 3380 3225 // It uses the current file offset defined in the file descriptor. 3381 // If the <extend> 16 MSB bits are non zero, it uses physical addressing:3382 // the physical address is computedas extend[15:0] | vaddr[31:0]3226 // If required by the <modes> argument, the physical address is computed 3227 // as extend[15:0] | vaddr[31:0] 3383 3228 // It increases the file size and allocate new clusters if (count + offset) 3384 3229 // is larger than the current file size. Then it loads and updates all … … 3397 3242 unsigned int vaddr, // source buffer vaddr 3398 3243 unsigned int count, // number of bytes to write 3399 unsigned int extend ) // physical address extension 3244 unsigned int extend, // physical address extension 3245 unsigned int modes ) // special modes 3400 3246 { 3401 3247 // checking FAT initialized … … 3443 3289 return GIET_FAT32_READ_ONLY; 3444 3290 } 3291 3292 // get special modes 3293 unsigned int physical_addressing = modes & FAT_PADDR_MODE; 3445 3294 3446 3295 // get file inode pointer and seek … … 3544 3393 unsigned char* cbuf; 3545 3394 fat_cache_desc_t* pdesc; 3546 if ( _ get_buffer_from_cache( inode,3395 if ( _fat_buffer_from_cache( inode, 3547 3396 cluster_id, 3548 3397 &pdesc ) ) … … 3590 3439 3591 3440 // move data 3592 if ( (extend & 0xFFFF0000)== 0 ) // no physical addressing3441 if ( physical_addressing == 0 ) // no physical addressing 3593 3442 { 3594 3443 char* source = (char*)(vaddr + done); … … 3598 3447 { 3599 3448 unsigned int flags; 3600 paddr_t psource = (((paddr_t) (extend & 0x0000FFFF))<<32) + vaddr + done;3449 paddr_t psource = (((paddr_t)extend)<<32) + vaddr + done; 3601 3450 paddr_t pdest = _v2p_translate( (unsigned int)dest , &flags ); 3602 3451 _physical_memcpy( pdest , psource , nbytes ); … … 4437 4286 else if ( !info.is_dir ) 4438 4287 { 4439 _printf("\n[FAT ERROR] _fat_readdir(): not a directory\n" );4288 _printf("\n[FAT ERROR] in _fat_readdir(): not a directory\n" ); 4440 4289 return GIET_FAT32_NOT_A_DIRECTORY; 4441 4290 } … … 4454 4303 while ( 1 ) 4455 4304 { 4456 if ( _fat_read( fd_id, (unsigned int)&buf, sizeof(buf), 0 ) != sizeof(buf) ) 4457 { 4458 _printf("\n[FAT ERROR] _fat_readdir(): can't read entry\n" ); 4305 if ( _fat_read( fd_id, 4306 (unsigned int)&buf, 4307 DIR_ENTRY_SIZE, 4308 0, 0, 0 ) != sizeof(buf) ) 4309 { 4310 _printf("\n[FAT ERROR] in _fat_readdir(): can't read entry\n" ); 4459 4311 return GIET_FAT32_IO_ERROR; 4460 4312 } … … 4516 4368 4517 4369 return GIET_FAT32_OK; 4518 } 4370 } // end _fat_readdir() 4519 4371 4520 4372 … … 4627 4479 4628 4480 4481 ////////////////////////////////////////////////////////////////////////////////// 4482 // The following function returns in the "desc" argument a pointer on a buffer 4483 // descriptor contained in a File_Cache, or in the Fat_Cache. 4484 // The searched buffer is idenfified by the "inode" and "cluster_id" arguments. 4485 // If the "inode" pointer is not NULL, the searched cache is a File-Cache. 4486 // If the "inode" pointer is NULL, the searched cache is the Fat-Cache, 4487 // The "cluster_id" argument is the buffer index in the file (or in the FAT). 4488 // In case of miss, it allocate a 4 Kbytes buffer and a cluster descriptor 4489 // from the local kernel heap, and calls the _fat_ioc_access() function to load 4490 // the missing cluster from the block device. 4491 ////////////////////////////////////////////////////////////////////////////////// 4492 // It returns 0 on success. 4493 // It returns 1 on error. 4494 ////////////////////////////////////////////////////////////////////////////////// 4495 int _fat_buffer_from_cache( fat_inode_t* inode, 4496 unsigned int cluster, 4497 fat_cache_desc_t** desc ) 4498 { 4499 // get cache pointer and levels 4500 fat_cache_node_t* node; // pointer on a 64-tree node 4501 unsigned int level; // cache level 4502 4503 if ( inode == NULL ) // searched cache is the Fat-Cache 4504 { 4505 node = _fat.fat_cache_root; 4506 level = _fat.fat_cache_levels; 4507 4508 #if (GIET_DEBUG_FAT & 1) 4509 if ( _get_proctime() > GIET_DEBUG_FAT ) 4510 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): enters in FAT-Cache" 4511 " for cluster = %d\n", cluster ); 4512 #endif 4513 } 4514 else // searched cache is a File-Cache 4515 { 4516 // add cache levels if needed 4517 while ( _get_levels_from_size( (cluster + 1) * 4096 ) > inode->levels ) 4518 { 4519 #if (GIET_DEBUG_FAT & 1) 4520 if ( _get_proctime() > GIET_DEBUG_FAT ) 4521 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): adding a File-Cache level\n" ); 4522 #endif 4523 4524 inode->cache = _allocate_one_cache_node( inode->cache ); 4525 inode->levels++; 4526 } 4527 4528 node = inode->cache; 4529 level = inode->levels; 4530 4531 #if (GIET_DEBUG_FAT & 1) 4532 if ( _get_proctime() > GIET_DEBUG_FAT ) 4533 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): enters in File-Cache <%s>" 4534 " for cluster = %d\n", inode->name , cluster ); 4535 #endif 4536 } 4537 4538 // search the 64-tree cache from top to bottom 4539 while ( level ) 4540 { 4541 // compute child index at each level 4542 unsigned int index = (cluster >> (6*(level-1))) & 0x3F; 4543 4544 if ( level == 1 ) // last level => children are cluster descriptors 4545 { 4546 fat_cache_desc_t* pdesc = (fat_cache_desc_t*)node->children[index]; 4547 4548 if ( pdesc == NULL ) // miss 4549 { 4550 // get missing cluster index lba 4551 unsigned int lba; 4552 unsigned int next; 4553 unsigned int current = inode->cluster; 4554 unsigned int count = cluster; 4555 4556 if ( inode == NULL ) // searched cache is the Fat-Cache 4557 { 4558 4559 #if (GIET_DEBUG_FAT & 1) 4560 if ( _get_proctime() > GIET_DEBUG_FAT ) 4561 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): miss in FAT-Cache for cluster %d\n", 4562 cluster ); 4563 #endif 4564 lba = _fat.fat_lba + (cluster << 3); 4565 } 4566 else // searched cache is a File-Cache 4567 { 4568 4569 #if (GIET_DEBUG_FAT & 1) 4570 if ( _get_proctime() > GIET_DEBUG_FAT ) 4571 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): miss in File-Cache <%s> " 4572 "for cluster %d\n", inode->name, cluster ); 4573 #endif 4574 while ( count ) 4575 { 4576 if ( _get_fat_entry( current , &next ) ) return 1; 4577 current = next; 4578 count--; 4579 } 4580 lba = _cluster_to_lba( current ); 4581 } 4582 4583 // allocate a 4 Kbytes buffer in cluster running the calling thread 4584 void* buf = _malloc( 4096 ); 4585 4586 // load one cluster (8 blocks) from block device 4587 if ( _fat_ioc_access( 1, // descheduling 4588 1, // to memory 4589 lba, 4590 (unsigned int)buf, 4591 8 ) ) 4592 { 4593 _free( buf ); 4594 _printf("\n[FAT ERROR] _fat_buffer_from_cache()" 4595 ": cannot access block device for lba = %x\n", lba ); 4596 return 1; 4597 } 4598 4599 // allocate buffer descriptor 4600 pdesc = _malloc( sizeof(fat_cache_desc_t) ); 4601 pdesc->lba = lba; 4602 pdesc->buffer = buf; 4603 pdesc->dirty = 0; 4604 node->children[index] = pdesc; 4605 4606 #if (GIET_DEBUG_FAT & 1) 4607 if ( _get_proctime() > GIET_DEBUG_FAT ) 4608 _printf("\n[DEBUG FAT] _fat_buffer_from_cache(): buffer loaded from device" 4609 " at vaddr = %x\n", (unsigned int)buf ); 4610 #endif 4611 } 4612 4613 // return pdesc pointer 4614 *desc = pdesc; 4615 4616 // prepare next iteration 4617 level--; 4618 } 4619 else // not last level => children are 64-tree nodes 4620 { 4621 fat_cache_node_t* child = (fat_cache_node_t*)node->children[index]; 4622 if ( child == NULL ) // miss 4623 { 4624 // allocate a cache node if miss 4625 child = _allocate_one_cache_node( NULL ); 4626 node->children[index] = child; 4627 } 4628 4629 // prepare next iteration 4630 node = child; 4631 level--; 4632 } 4633 } // end while 4634 4635 return 0; 4636 } // end _fat_buffer_from_cache() 4637 4638 4639 4640 4629 4641 // Local Variables: 4630 4642 // tab-width: 4
Note: See TracChangeset
for help on using the changeset viewer.