Leaked source code of windows server 2003
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.

692 lines
17 KiB

  1. /*
  2. * Program: Recursive Directory listing
  3. * Author: Steve Salisbury
  4. *
  5. * Last Modified:
  6. *
  7. * 1995-03-08 Wed 16:00 PST
  8. * **** >>>> Ported to Win32 <<<< ****
  9. */
  10. #ifdef _WIN32
  11. #define WIN32
  12. #endif
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <windows.h>
  17. #ifdef _DEBUG
  18. int DebugFlag ;
  19. #define DEBUG(n,x) if ( DebugFlag & n ) printf x;
  20. #else
  21. #define DEBUG(n,x)
  22. #endif
  23. #define ISSLASH(ch) ((ch) == '/' || (ch) == '\\')
  24. #define BL ' '
  25. #define NAMLEN 8
  26. #define EXTLEN 3
  27. typedef unsigned char uchar ;
  28. typedef unsigned short ushort ;
  29. typedef unsigned int uint ;
  30. typedef unsigned long ulong ;
  31. #define ATTRIB_READONLY 0x01
  32. #define ATTRIB_HIDDEN 0x02
  33. #define ATTRIB_SYSTEM 0x04
  34. #define ATTRIB_VOLUMELABEL 0x08
  35. #define ATTRIB_DIRECTORY 0x10
  36. #define ATTRIB_ARCHIVE 0x20
  37. #define ATTRIB_ALL ( ATTRIB_HIDDEN | ATTRIB_SYSTEM | ATTRIB_DIRECTORY )
  38. void PrintFile ( WIN32_FIND_DATA * match ) ;
  39. #define MAXPATHLENGTH (_MAX_PATH+4)
  40. char path [ MAXPATHLENGTH ] ;
  41. char current_dir [ MAXPATHLENGTH ] ; /* Current Directory */
  42. int pathlength ;
  43. uint clustersize ;
  44. uint sectorsize ;
  45. uint availclusters ;
  46. uint totalclusters ;
  47. int numfiles ;
  48. int numdirs ;
  49. long numbytes ;
  50. long numclusters ;
  51. uint NewClusterSize ; /* override actual cluster size */
  52. uint NewSectorSize ; /* override actual sector size */
  53. int maxwidth = 71 ; /* Maximum width of an output line */
  54. char totalstring [] =
  55. "[ %s files, %s sub-dirs, %s bytes (%s allocated) ]\n" ;
  56. int AltNameFlag ; /* If non-zero, echo 8.3 names as well */
  57. int DirOnlyFlag ; /* If non-zero, only directories are listed */
  58. int FileOnlyFlag ; /* If non-zero, only files are listed */
  59. int TerseFlag ; /* If non-zero, output is very terse */
  60. int SummaryOnlyFlag ; /* If non-zero, output ONLY summary information */
  61. int NoSummaryFlag ; /* If non-zero, do not output summary information */
  62. uint Exclude ; /* file attributes to excluded from display */
  63. uint Require ; /* file attributes to be required for display */
  64. char * VolumeLabel ( char * driveString , unsigned * serialNum ) ;
  65. void PrintDir ( void ) ;
  66. void PrintFile ( WIN32_FIND_DATA * match ) ;
  67. int get_drive ( void ) ;
  68. void get_dir ( char * buffer , int drive ) ;
  69. int get_free ( char * driveString , uint * availp , uint * secsizep , uint * totalp ) ;
  70. char * PrintWithCommas ( unsigned n ) ;
  71. int main ( int argc , char * * argv )
  72. {
  73. char * ap ; /* ap = *argv when parsing the switch args */
  74. char * volume ;
  75. int drive = get_drive ( ) ;
  76. char driveString [ _MAX_PATH ] ;
  77. uint serialNum ;
  78. ++ argv , -- argc ;
  79. #ifdef _DEBUG
  80. if ( argc > 0 && argv [ 0 ] [ 0 ] == '-' && argv [ 0 ] [ 1 ] == 'D' )
  81. {
  82. char * endptr ;
  83. DebugFlag = strtoul ( argv [ 0 ] + 2 , & endptr , 0 ) ;
  84. printf("DebugFlag = 0x%x (%s)\n" , DebugFlag , * argv ) ;
  85. ++ argv , -- argc ;
  86. }
  87. #endif
  88. while ( argc > 0 && * ( ap = * argv ) == '-' )
  89. {
  90. while ( * ++ ap )
  91. if ( * ap == 'a' )
  92. {
  93. int flag ;
  94. if ( * ++ ap != '-' && * ap != '=' )
  95. goto Usage ;
  96. flag = * ap ;
  97. while ( * ++ ap )
  98. {
  99. if ( * ap == 'a' || * ap == 'A' )
  100. if ( flag == '-' )
  101. Exclude |= ATTRIB_ARCHIVE ;
  102. else
  103. Require |= ATTRIB_ARCHIVE ;
  104. else if ( * ap == 'r' || * ap == 'R' )
  105. if ( flag == '-' )
  106. Exclude |= ATTRIB_READONLY ;
  107. else
  108. Require |= ATTRIB_READONLY ;
  109. else if ( * ap == 'h' || * ap == 'H' )
  110. if ( flag == '-' )
  111. Exclude |= ATTRIB_HIDDEN ;
  112. else
  113. Require |= ATTRIB_HIDDEN ;
  114. else if ( * ap == 's' || * ap == 'S' )
  115. if ( flag == '-' )
  116. Exclude |= ATTRIB_SYSTEM ;
  117. else
  118. Require |= ATTRIB_SYSTEM ;
  119. else if ( * ap == '-' || * ap == '=' )
  120. flag = * ap ;
  121. else
  122. goto Usage ;
  123. }
  124. -- ap ;
  125. }
  126. else if ( * ap == 'c' )
  127. { /* Use alternate cluster size */
  128. while ( isdigit ( * ++ ap ) )
  129. NewClusterSize = NewClusterSize * 10 + * ap - '0' ;
  130. printf ( "New ClusterSize = %u\n" , NewClusterSize ) ;
  131. -- ap ;
  132. }
  133. else if ( * ap == 'd' )
  134. /* Print directories but not files */
  135. ++ DirOnlyFlag ;
  136. else if ( * ap == 'f' )
  137. /* Print directories but not files */
  138. ++ FileOnlyFlag ;
  139. else if ( * ap == 's' )
  140. { /* Use alternate sector size */
  141. while ( isdigit ( * ++ ap ) )
  142. NewSectorSize = NewSectorSize * 10 + * ap - '0' ;
  143. printf ( "NewSectorSize = %u\n" , NewSectorSize ) ;
  144. -- ap ;
  145. }
  146. else if ( * ap == 'z' )
  147. /* Display ONLY summary info. */
  148. ++ SummaryOnlyFlag ;
  149. else if ( * ap == 'Z' )
  150. /* Display no summary info. */
  151. ++ NoSummaryFlag ;
  152. else if ( * ap == 't' )
  153. /* Only file/dir names in output */
  154. ++ TerseFlag ;
  155. else if ( * ap == 'x' )
  156. /* Show 8.3 names */
  157. ++ AltNameFlag ;
  158. else
  159. goto Usage ;
  160. -- argc ;
  161. ++ argv ;
  162. }
  163. if ( argc > 1 )
  164. {
  165. Usage:
  166. puts (
  167. #ifdef _DEBUG
  168. "usage: pd [-D#] [ -dftxzZ -a-* -a=* -s# -c# ] [path]\n"
  169. #else
  170. "usage: pd " "[ -dftxzZ -a-* -a=* -s# -c# ] [path]\n"
  171. #endif
  172. "\twhere path is an optional Path to a directory\n"
  173. #ifdef _DEBUG
  174. "\t`-D#' means print debugging information (# is a number\n"
  175. "\t\twhich is interpreted as a bit mask for debug info.)\n"
  176. #endif
  177. "\t`-d' means print only directory names\n"
  178. "\t`-f' means print only file names\n"
  179. "\t`-t' means terse output (only file/directory name)\n"
  180. "\t`-x' means show 8.3 alternate names after long filenames\n"
  181. "\t`-z' means output only summary information\n"
  182. "\t`-Z' means do not output any summary information\n"
  183. "\t`-a-* means exclude files with attribute(s) * (out of ARHS)\n"
  184. "\t`-a=* means show only files with attribute(s) *\n"
  185. "\t the possible attributes are ARHS\n"
  186. "\t`-c#' sets logical cluster size to # sectors\n"
  187. "\t`-s#' sets logical sector size to # bytes\n"
  188. ) ;
  189. exit ( 1 ) ;
  190. }
  191. path [ 0 ] = drive + '@' ;
  192. path [ 1 ] = ':' ;
  193. path [ 2 ] = '\\' ;
  194. path [ 3 ] = '\0' ;
  195. strcpy ( driveString , path ) ;
  196. if ( argc == 1 )
  197. {
  198. char * arg = argv [ 0 ] ;
  199. if ( isalpha ( arg [ 0 ] ) && arg [ 1 ] == ':' )
  200. {
  201. drive = toupper ( * arg ) ;
  202. if ( isalpha ( drive ) )
  203. drive -= 'A' - 1 ;
  204. else
  205. {
  206. fprintf ( stderr , "pd: expected alphabetic character before :\n\t%s\n" , arg ) ;
  207. exit ( 1 ) ;
  208. }
  209. driveString [ 0 ] = path [ 0 ] = * arg ;
  210. if ( arg [ 2 ] )
  211. /* Specified Directory & Directory */
  212. strcpy ( path + 2 , arg + 2 ) ;
  213. else /* Specified Drive, Current Directory */
  214. get_dir ( path + 3 , drive ) ;
  215. }
  216. else if ( ISSLASH ( arg [ 0 ] ) && ISSLASH ( arg [ 1 ] ) )
  217. {
  218. int n = 2 ;
  219. /*-
  220. * Find the slash that terminates the server name
  221. -*/
  222. while ( arg [ n ] && ! ISSLASH ( arg [ n ] ) )
  223. ++ n ;
  224. if ( ! arg [ n ] )
  225. {
  226. fprintf ( stderr , "pd: expected server name plus share point:\n\t%s\n" , * argv ) ;
  227. exit ( 1 ) ;
  228. }
  229. ++ n ;
  230. /*-
  231. * Find the slash that terminates the share point
  232. -*/
  233. while ( arg [ n ] && ! ISSLASH ( arg [ n ] ) )
  234. ++ n ;
  235. if ( ! arg [ n ] )
  236. {
  237. fprintf ( stderr , "pd: expected share point name after server name:\n\t%s\n" , * argv ) ;
  238. exit ( 1 ) ;
  239. }
  240. ++ n ;
  241. strcpy ( path , arg ) ;
  242. strcpy ( driveString , arg ) ;
  243. driveString [ n ] = '\0' ;
  244. }
  245. else /* Current Drive, Specified Directory */
  246. strcpy ( path + 2 , arg ) ;
  247. }
  248. else /* Current Drive & Directory */
  249. get_dir ( path + 3 , drive ) ;
  250. DEBUG(1, ("path = \"%s\"\n",path))
  251. DEBUG(1, ("driveString = \"%s\"\n",driveString))
  252. volume = VolumeLabel ( driveString , & serialNum ) ;
  253. if ( ! NoSummaryFlag )
  254. {
  255. printf ( "Directory %s " , path ) ;
  256. if ( * volume )
  257. printf ( "(Volume = \"%s\", %04X-%04X)\n" , volume ,
  258. ( serialNum >> 16 ) & 0xFFFF , serialNum & 0xFFFF ) ;
  259. else
  260. printf ( "(No Volume Label, %04X-%04X)\n" ,
  261. ( serialNum >> 16 ) & 0xFFFF , serialNum & 0xFFFF ) ;
  262. }
  263. clustersize = get_free ( driveString , & availclusters , & sectorsize ,
  264. & totalclusters ) ;
  265. if ( NewClusterSize )
  266. clustersize = NewClusterSize ;
  267. if ( NewSectorSize )
  268. sectorsize = NewSectorSize ;
  269. if ( ! sectorsize )
  270. {
  271. fprintf ( stderr , "pd: warning: assuming 512 bytes/sector.\n" ) ;
  272. sectorsize = 512 ;
  273. }
  274. if ( ! clustersize )
  275. {
  276. fprintf ( stderr , "pd: warning: assuming 1 sector/cluster.\n" ) ;
  277. clustersize = 1 ;
  278. }
  279. pathlength = strlen ( path ) ;
  280. if ( path [ pathlength - 1 ] == '\\' )
  281. -- pathlength ; /* Make "\" visible but not present */
  282. PrintDir ( ) ;
  283. if ( ! NoSummaryFlag )
  284. printf ( totalstring , PrintWithCommas ( numfiles ) ,
  285. PrintWithCommas ( numdirs ) ,
  286. PrintWithCommas ( numbytes ) ,
  287. PrintWithCommas ( numclusters * clustersize *
  288. sectorsize ) ) ;
  289. return 0 ;
  290. }
  291. /*
  292. * VolumeLabel -
  293. * Get the volume Label
  294. * This routine may NOT return NULL
  295. */
  296. static char volume [ _MAX_PATH ] = "12345678901" ;
  297. char * VolumeLabel ( char * driveString , unsigned * pSerialNumber )
  298. {
  299. uint MaxCompLength ;
  300. uint FSflags ;
  301. if ( ! GetVolumeInformation ( driveString , volume , sizeof ( volume ) ,
  302. pSerialNumber , & MaxCompLength , & FSflags , NULL , 0 ) )
  303. {
  304. fprintf ( stderr , "pd: unexpected error (%d) from GetVolumeInformation(%s)\n" ,
  305. GetLastError() , driveString ) ;
  306. exit ( 1 ) ;
  307. }
  308. DEBUG(2, ("%s: \"%s\" : %04X-%04X; %d c; 0x%X\n",driveString,volume,
  309. (*pSerialNumber>>16)&0xFFFF,*pSerialNumber&0xFFFF,FSflags))
  310. return volume ;
  311. }
  312. /*
  313. * PrintDir -
  314. * Print all the files in the current directory
  315. * Then recursively print the sub-directories
  316. * Ignore the "." and ".." special entries
  317. */
  318. void PrintDir ( void )
  319. {
  320. WIN32_FIND_DATA match ;
  321. HANDLE handle ;
  322. int flag ;
  323. path [ pathlength ] = '\\' ;
  324. path [ pathlength + 1 ] = '*' ;
  325. path [ pathlength + 2 ] = '\0' ;
  326. handle = FindFirstFile ( path , & match ) ;
  327. flag = handle != INVALID_HANDLE_VALUE ;
  328. DEBUG(4, ("PrintDir - opening handle %08X (files)\n",handle))
  329. path [ pathlength ] = '\0' ; /* Truncate to original path */
  330. while ( flag )
  331. {
  332. DEBUG(4, ("PrintDir - FindFirst/NextFile(\"%s\") (files)\n",match.cFileName))
  333. /* Print everything in the directory except "." and ".." */
  334. if ( ATTRIB_DIRECTORY & ~ match . dwFileAttributes )
  335. PrintFile ( & match ) ;
  336. flag = FindNextFile ( handle , & match ) ;
  337. }
  338. FindClose ( handle ) ;
  339. DEBUG(4, ("PrintDir - closing handle %08X (files)\n",handle))
  340. path [ pathlength ] = '\\' ; /* Restore to "...\*" */
  341. handle = FindFirstFile ( path , & match ) ;
  342. flag = handle != INVALID_HANDLE_VALUE ;
  343. DEBUG(8, ("PrintDir - opening handle %08X (dirs)\n",handle))
  344. path [ pathlength ] = '\0' ; /* Truncate to original path */
  345. while ( flag )
  346. {
  347. char * cp ;
  348. int lensave ;
  349. DEBUG(8, ("PrintDir - FindFirst/NextFile(\"%s\") (dirs)\n",match.cFileName))
  350. /* Find all sub-directories except "." and ".." */
  351. if ( ( match . dwFileAttributes & ATTRIB_DIRECTORY )
  352. && strcmp ( match . cFileName , "." )
  353. && strcmp ( match . cFileName , ".." ) )
  354. {
  355. PrintFile ( & match ) ;
  356. cp = match . cFileName ;
  357. lensave = pathlength ;
  358. /* Add "\dirname" to the current Path */
  359. path [ pathlength ++ ] = '\\' ;
  360. while ( path [ pathlength ] = * cp ++ )
  361. ++ pathlength ;
  362. PrintDir ( ) ;
  363. path [ pathlength = lensave ] = '\0' ;
  364. }
  365. flag = FindNextFile ( handle , & match ) ;
  366. }
  367. FindClose ( handle ) ;
  368. DEBUG(8, ("PrintDir - closing handle %08X (dirs)\n",handle))
  369. }
  370. /* static char * months [ ] = {
  371. /* "?00" , "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" ,
  372. /* "Aug" , "Sep" , "Oct" , "Nov" , "Dec" , "?13" , "?14" , "?15" } ;
  373. */
  374. /* static char * weekdays [ ] = {
  375. /* "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" } ;
  376. */
  377. static char monthstarts [ ] =
  378. /**** Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
  379. { -1 , 0 , 3 , 3 , 6 , 1 , 4 , 6 , 2 , 5 , 0 , 3 , 5 , -1 , -1 , -1 } ;
  380. /*
  381. * PrintFile -
  382. * Print the information for the file described in "match"
  383. */
  384. void PrintFile ( WIN32_FIND_DATA * match )
  385. {
  386. int year , month , day /* , wkday */ ;
  387. int hour , minute , second ;
  388. long sec , clu ;
  389. char sizebuf [ 12 ] ; /* Either size of file or else "****DIR****" */
  390. FILETIME lftime ;
  391. SYSTEMTIME systime ;
  392. /*
  393. * If only directories are to be shown, do not list files
  394. * and if only files are to be shown, do not list directories
  395. */
  396. if ( ( DirOnlyFlag && ! ( match -> dwFileAttributes & ATTRIB_DIRECTORY ) )
  397. || ( FileOnlyFlag && ( match -> dwFileAttributes & ATTRIB_DIRECTORY ) ) )
  398. return ;
  399. /*
  400. ** Check the attribute filters
  401. */
  402. if ( ( match -> dwFileAttributes & Exclude )
  403. || ( match -> dwFileAttributes & Require ) != Require )
  404. return ;
  405. /*
  406. ** At this point, count this file and its bytes
  407. */
  408. if ( match -> dwFileAttributes & ATTRIB_DIRECTORY )
  409. ++ numdirs ;
  410. else
  411. ++ numfiles ;
  412. sec = ( match -> nFileSizeLow + sectorsize - 1 ) / sectorsize ;
  413. clu = ( sec + clustersize - 1 ) / clustersize ;
  414. numbytes += match -> nFileSizeLow ;
  415. numclusters += clu ;
  416. if ( SummaryOnlyFlag )
  417. return ;
  418. FileTimeToLocalFileTime ( & match -> ftLastWriteTime , & lftime ) ;
  419. FileTimeToSystemTime ( & lftime , & systime ) ;
  420. year = systime . wYear ;
  421. month = systime . wMonth ;
  422. day = systime . wDay ;
  423. hour = systime . wHour ;
  424. minute = systime . wMinute ;
  425. second = systime . wSecond ;
  426. /*
  427. * 1980 Jan 01 was a Tuesday (2):
  428. * Add in the day of the month and the month offsets
  429. * Add 1 day for each year since 1980
  430. * Add 1 for each leap year since 1980
  431. */
  432. /* wkday = 2 + ( day - 1 ) + monthstarts [ month ] +
  433. /* /* year + leap years before the most recent */
  434. /* ( year - 1980 ) + ( ( year - 1980 ) >> 2 ) +
  435. /* /* Add in the most recent leap day */
  436. /* ( ( ( year & 3 ) != 0 || month > 2 ) ? 1 : 0 ) ;
  437. /* wkday %= 7 ;
  438. */
  439. if ( TerseFlag )
  440. printf ( "%s\\%s%s\n" ,
  441. path , match->cFileName ,
  442. match -> dwFileAttributes & ATTRIB_DIRECTORY ? "\\" : "" ) ;
  443. else
  444. {
  445. char altbuf [ 24 ] ; /* used to display alternate (8.3) name */
  446. if ( match -> dwFileAttributes & ATTRIB_DIRECTORY )
  447. strcpy ( sizebuf , "****DIR****" ) ;
  448. else if ( match -> nFileSizeLow <= 999999999L )
  449. strcpy ( sizebuf , PrintWithCommas ( match -> nFileSizeLow ) ) ;
  450. else /* File too big for 9 digits */
  451. sprintf ( sizebuf , "%s K" , PrintWithCommas ( ( match -> nFileSizeLow + 1023 ) / 1024 ) ) ;
  452. if ( AltNameFlag && * match -> cAlternateFileName )
  453. sprintf ( altbuf , " [%s]" , match->cAlternateFileName ) ;
  454. else
  455. altbuf [ 0 ] = '\0' ;
  456. printf ( "%11s %04d-%02d-%02d %02d:%02d:%02d %c%c%c%c %s\\%s%s%s\n" ,
  457. sizebuf , year , month , day , hour , minute , second ,
  458. match -> dwFileAttributes & ATTRIB_ARCHIVE ? 'A' : '-' ,
  459. match -> dwFileAttributes & ATTRIB_READONLY ? 'R' : '-' ,
  460. match -> dwFileAttributes & ATTRIB_HIDDEN ? 'H' : '-' ,
  461. match -> dwFileAttributes & ATTRIB_SYSTEM ? 'S' : '-' ,
  462. path , match->cFileName ,
  463. match -> dwFileAttributes & ATTRIB_DIRECTORY ? "\\" : "" ,
  464. altbuf ) ;
  465. }
  466. }
  467. /*
  468. * Return the Current Disk Drive (1=A, 2=B, etc.)
  469. * Note that DOS uses (0=A,1=B, etc.) for this call
  470. */
  471. int get_drive ( void )
  472. {
  473. char CurDir [ _MAX_PATH + 4 ] ;
  474. int lenCurDir = _MAX_PATH ;
  475. int drive ;
  476. if ( ! GetCurrentDirectory ( lenCurDir , CurDir ) )
  477. {
  478. fprintf ( stderr , "pd: unexpected error (%d) from GetCurrentDirector\n" ,
  479. GetLastError() ) ;
  480. exit ( 1 ) ;
  481. }
  482. drive = toupper ( CurDir [ 0 ] ) - ( 'A' - 1 ) ;
  483. DEBUG(1, ("get_drive => %d (%c:)\n", drive , CurDir [ 0 ]))
  484. return drive ;
  485. }
  486. /*
  487. * Store the Current Directory in the given char buffer
  488. * The leading "\" in the path is not stored.
  489. * The string is terminated by a null.
  490. */
  491. void get_dir ( char * buffer , int drive )
  492. {
  493. char CurDir [ _MAX_PATH + 4 ] ;
  494. int lenCurDir = _MAX_PATH ;
  495. if ( ! GetCurrentDirectory ( lenCurDir , CurDir ) )
  496. {
  497. fprintf ( stderr , "pd: unexpected error (%d) from GetCurrentDirectory\n" ,
  498. GetLastError() ) ;
  499. exit ( 1 ) ;
  500. }
  501. strcpy ( buffer , CurDir + 3 ) ;
  502. DEBUG(1, ("get_dir => \"%s\"\n", buffer ))
  503. }
  504. /*
  505. * get_free - returns the number of sectors per cluster
  506. * and stores the number of available clusters,
  507. * the size of a sector (in bytes), and the total
  508. * number of clusters on the current drive
  509. */
  510. int get_free ( char * driveString , uint * availp , uint * secsizep , uint * totalp )
  511. {
  512. unsigned int SectorsPerCluster , BytesPerSector , FreeClusters , Clusters ;
  513. if ( ! GetDiskFreeSpace ( driveString , & SectorsPerCluster ,
  514. & BytesPerSector , & FreeClusters , & Clusters ) )
  515. {
  516. fprintf ( stderr , "pd: unexpected error (%d) from GetDiskFreeSpace ( %s )\n" ,
  517. GetLastError() , driveString ) ;
  518. exit ( 1 ) ;
  519. }
  520. * availp = FreeClusters ;
  521. * totalp = Clusters ;
  522. * secsizep = BytesPerSector ;
  523. DEBUG(1, ("get_free => Clusters %d/%d, %d * %d\n",*availp,*totalp,*secsizep,SectorsPerCluster))
  524. return SectorsPerCluster ;
  525. }
  526. char * PrintWithCommas ( unsigned n )
  527. {
  528. static char buffers [ 16 ] [ 16 ] ;
  529. static int bufnumber ;
  530. char * p = buffers [ bufnumber ++ % 16 ] ;
  531. if ( n <= 999 )
  532. sprintf ( p , "%d" , n ) ;
  533. else if ( n <= 999999 )
  534. sprintf ( p , "%d,%03d" , n / 1000 , n % 1000 ) ;
  535. else if ( n <= 999999999 )
  536. sprintf ( p , "%d,%03d,%03d" , n / 1000000 , n / 1000 % 1000 , n % 1000 ) ;
  537. else
  538. sprintf ( p , "%d,%03d,%03d,%03d" , n / 1000000000 , n / 1000000 % 1000 , n / 1000 % 1000 , n % 1000 ) ;
  539. return p ;
  540. }